<?xml version="1.0" encoding="UTF-8"?>
<rfc category="exp" docName="draft-asadi-tcp-spa-00" ipr="trust200902" submissionType="IETF">
  <front>
    <title abbrev="TCP In-Band SPA">TCP In-Band Single Packet Authentication (TCP-SPA)</title>
    <seriesInfo name="Internet-Draft" value="draft-asadi-tcp-spa-00"/>
    <seriesInfo name="Intended status" value="Experimental"/>
    <author fullname="Amin Asadi">
      <organization>Individual</organization>
      <address>
        <email>aminassadi.og@gmail.com</email>
      </address>
    </author>
    <date year="2026" month="February" day="23"/>
    <abstract>
      <t>This document describes a mechanism called TCP In-Band Single Packet
      Authentication (TCP-SPA).  A client that wishes to open a TCP
      connection to a protected server embeds a compact Message
      Authentication Code (MAC) inside the TCP SYN packet itself, carried
      as an experimental TCP option (kind 253, per <xref target="RFC6994"/>).  The server
      verifies the MAC at the earliest possible point in the network stack
      — before a socket is allocated or the TCP handshake proceeds — and
      drops the SYN if verification fails.</t>
      <t>This approach differs from existing Single Packet Authentication
      (SPA) systems in that no out-of-band authentication packet is
      required: authentication and connection establishment are a single
      atomic step.</t>
    </abstract>
  </front>
  <middle>
    <section title="Introduction and Problem Statement" anchor="intro">
      <t>Internet-facing TCP services are continuously probed by scanners,
      bots, and opportunistic attackers.  Even when a service is properly
      hardened — strong passwords, TLS, privilege separation — the act of
      completing or even initiating a TCP handshake consumes server
      resources: socket allocation, kernel scheduler wakeups, TLS state
      machine initialization.  At scale, or under a targeted SYN-flood,
      this cost becomes significant.</t>
      <t>The ideal property would be: a server never allocates any resource
      for a connection until it has cryptographic proof that the remote
      party holds a valid secret.  In particular, the proof should arrive
      in the very first packet — the TCP SYN — so that an illegitimate SYN
      can be discarded at the lowest possible layer without ever touching
      the socket layer.</t>
      <t>Additional desiderata:</t>
      <list style="letters">
        <t>The mechanism MUST NOT require any out-of-band communication
        channel, pre-flight packet, or extra round trip.  Authentication
        and connection establishment must be a single atomic operation
        from the client's perspective.</t>
        <t>The mechanism MUST be transparent to the application.  A
        standard connect(2) call, with no modification to the
        application, should automatically carry the authentication token.</t>
        <t>The mechanism MUST be NAT traversal-friendly.  Many legitimate
        clients are behind one or more Network Address Translators.  Any
        mechanism that binds authentication to the client's IP address
        creates a timing race when NAT rewrites the address and breaks
        address-bound tokens.</t>
        <t>The mechanism SHOULD allow key rotation without service
        interruption.</t>
        <t>The cryptographic overhead per SYN MUST be small enough to run
        inside a kernel data path (e.g., XDP or TC eBPF) without
        measurable latency impact.</t>
      </list>
      <t>No existing IETF-standardized mechanism satisfies all five
      properties simultaneously.  <xref target="existing"/> explains why.</t>
    </section>
    <section title="Why Existing Mechanisms Do Not Solve This Problem" anchor="existing">
      <section title="TCP MD5 Signature Option (RFC 2385)" anchor="md5">
        <t><xref target="RFC2385"/> defines a TCP option (kind 19) that carries an MD5
        signature over the full TCP segment, the IP pseudo-header, and a
        shared secret.  It was designed to protect BGP sessions between
        routers in a known topology.</t>
        <t>It fails the stated requirements for several reasons:</t>
        <t><strong>NAT incompatibility (violates property c):</strong>  The MAC input includes
        the source and destination IP addresses from the IP pseudo-header.
        A NAT device that rewrites the source address after the client
        has computed the signature will cause verification to fail at the
        server.  There is no way to make <xref target="RFC2385"/> work through NAT without
        defeating its security guarantee.</t>
        <t><strong>Wrong threat model (different problem):</strong>  <xref target="RFC2385"/> is designed to
        prevent session hijacking on a long-lived BGP session between two
        known peers.  It authenticates every segment, including data
        segments, and requires both peers to share the same key for the
        lifetime of the session.  The problem addressed here is
        authenticating the initiating SYN from an arbitrary client before
        any session state is created.  These are fundamentally different
        problems.</t>
        <t><strong>MD5 is no longer considered secure:</strong>  MD5 is cryptographically broken.
        <xref target="RFC5925"/> was written specifically to replace <xref target="RFC2385"/>.</t>
      </section>
      <section title="TCP Authentication Option, TCP-AO (RFC 5925)" anchor="tcp-ao">
        <t><xref target="RFC5925"/> replaces <xref target="RFC2385"/> with a more modern construction.  It
        supports multiple MAC algorithms, master keys and traffic keys (via a
        key derivation function defined in <xref target="RFC5926"/>), and key rotation.</t>
        <t>It still fails the stated requirements:</t>
        <t><strong>NAT incompatibility (violates property c):</strong>  Like its predecessor,
        TCP-AO includes the IP source and destination addresses in the MAC
        computation (Section 5.1.3 of <xref target="RFC5925"/>, the "connection
        identifier").  A NAT that rewrites the source address breaks
        authentication.  <xref target="RFC5925"/> explicitly notes (Section 7) that NAT
        is incompatible with TCP-AO unless the NAT also updates the MAC,
        which is impossible without the shared key — defeating the purpose.</t>
        <t><strong>Designed for peer authentication, not port authorization (different
        problem):</strong>  TCP-AO secures an established TCP session between two
        long-lived peers (typically routers or servers) whose addresses
        and keys are known in advance.  The problem addressed here is
        pre-authentication of arbitrary clients attempting to initiate a
        new connection, where the client population changes dynamically
        and may be behind NAT.</t>
        <t><strong>Requires kernel support and configuration on both ends:</strong>  TCP-AO
        must be configured via socket options before the connection is
        established.  This requires application modification (violates
        property b) or OS-level transparent interception, which TCP-AO
        was not designed to support.</t>
        <t>In summary: TCP-AO is an excellent solution to the problem it was
        designed for (securing BGP and similar sessions between known peers).
        It is not a solution to pre-connection port authorization for
        arbitrary, NAT-traversing clients.</t>
      </section>
      <section title="Out-of-Band SPA (e.g., fwknop)" anchor="fwknop">
        <t>The most widely deployed SPA implementation is fwknop <xref target="FWKNOP"/>.
        Its protocol, while not an IETF standard, is the reference point for
        the SPA concept.  The model is:</t>
        <ol>
          <li>The client sends a specially crafted UDP packet (or ICMP
          packet, or similar) to the server.  This packet contains a
          token: a MAC over the client's IP address, a timestamp, and a
          shared secret.</li>
          <li>The server's SPA daemon (running in userspace, monitoring a raw
          socket or pcap) receives the packet, verifies the token, and
          temporarily installs a firewall rule to open the target port for
          the source IP of the packet.</li>
          <li>The client then initiates a normal TCP connection.</li>
        </ol>
        <t>This fails properties (a) and (c):</t>
        <t><strong>Requires an extra round trip (violates property a):</strong>  Steps 1-2 must
        complete before step 3 can succeed.  The client needs a separate
        tool (not just connect(2)) to send the SPA packet.  There is an
        unavoidable timing window between firewall-rule installation and
        the TCP SYN arriving.</t>
        <t><strong>Source-IP binding is NAT-hostile (violates property c):</strong>  The firewall
        rule opened in step 2 is keyed to the source IP seen in the SPA
        UDP packet.  If the client is behind a NAT that uses a different
        source IP for UDP and TCP traffic (e.g., due to NAPT hairpinning
        or load balancing), the TCP SYN will arrive from a different
        address than the one whitelisted, and the connection will be
        refused.  In practice, fwknop deployments work around this by
        accepting a timing window long enough to account for NAT, which
        widens the attack surface.</t>
        <t><strong>Plaintext source IP in the token (additional concern):</strong>  The token
        explicitly encodes the client's IP address.  This means the
        authenticating data is not independent of NAT-rewritten fields.</t>
      </section>
      <section title="Port Knocking" anchor="port-knocking">
        <t>Port knocking predates SPA.  The client sends a sequence of packets
        to a sequence of closed ports; the server observes the sequence and
        opens a port.</t>
        <t>It is entirely unauthenticated in the cryptographic sense: any
        observer who captures the sequence can replay it.  Some variants add
        encryption, but the fundamental fragility remains.  Port knocking is
        not considered a serious candidate for the requirements in <xref target="intro"/> and is mentioned only for completeness.</t>
      </section>
    </section>
    <section title="Proposed Solution: TCP In-Band SPA" anchor="solution">
      <t>      TCP In-Band SPA embeds the authentication token inside the TCP SYN
      packet as a TCP option, using kind 253 (the first experimental kind
      reserved by <xref target="RFC6994"/>) with a registered ExID to avoid collisions with
      other experiments.</t>
      <t>The high-level model is:</t>
      <ol>
        <li>A client that wants to connect to a protected server computes
        a SipHash-2-4 MAC over a small input struct that includes a
        coarse timestamp, a Key ID, and the TCP Initial Sequence Number
        (ISN) of the SYN.</li>
        <li>The client writes this MAC, along with the Key ID, version, and
        timestamp, into a TCP option field of the outgoing SYN.  This
        is done transparently inside the kernel (e.g., via a TC eBPF
        egress program), requiring no application modification.</li>
        <li>The server, at the earliest possible processing point (e.g.,
        XDP), reads the TCP option, looks up the corresponding key by
        Key ID, recomputes the expected MAC, and either passes or drops
        the SYN.</li>
      </ol>
      <t>This satisfies all five properties from <xref target="intro"/>:</t>
      <list style="letters">
        <t><strong>Single step:</strong> the SYN itself carries the proof.  No pre-flight
        packet is needed.</t>
        <t><strong>Transparent:</strong> the TC eBPF hook injects the option before the
        packet leaves the NIC; no application change is required.</t>
        <t><strong>NAT friendly:</strong> the MAC input does NOT include the source IP
        address.  A NAT that rewrites the source IP does not invalidate
        the MAC.  The TCP ISN, which is carried in the SYN and is not
        rewritten by standard NAT, ties the token to the specific
        connection.</t>
        <t><strong>Key rotation:</strong> the Key ID field allows multiple keys to coexist.
        A client can use a new key immediately; the server keeps old keys
        until they expire.</t>
        <t><strong>Lightweight:</strong> SipHash-2-4 over 14 bytes is a handful of XOR and
        rotate operations, well within the cycle budget of an XDP program.</t>
      </list>
    </section>
    <section title="TCP Option Format" anchor="format">
      <t>The SPA option is carried as a TCP option with kind 253 (experimental,
      <xref target="RFC6994"/>) and the following layout:</t>
      <figure anchor="option-format">
        <artwork>
<![CDATA[
 0               1               2               3
 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Kind (253)  |  Length (20)  |       ExID (2 octets)         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Version (1B) | Reserved (1B) |       Key ID (2 octets)       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      Time Step (4 octets)                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                   Authentication Tag (8 octets)               |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
]]>
        </artwork>
      </figure>
      <t>Total option length: 20 octets.</t>
      <t>Fields:</t>
      <dl>
        <dt>Kind (1 octet)</dt>
        <dd>253, as defined in <xref target="RFC6994"/>.</dd>
        <dt>Length (1 octet)</dt>
        <dd>20 (total option length including Kind and Length octets).</dd>
        <dt>ExID (2 octets)</dt>
        <dd>Experiment Identifier, network byte order.  Disambiguates this experiment from other users
        of kind 253, per <xref target="RFC6994"/>.  A value of 0x0001
        is used in the current implementation; a formal
        assignment from IANA would be sought for any
        Standards Track version of this specification.</dd>
        <dt>Version (1 octet)</dt>
        <dd>Protocol version.  MUST be 1 in this version of the specification.</dd>
        <dt>Reserved (1 octet)</dt>
        <dd>MUST be zero on send; ignored on receive.</dd>
        <dt>Key ID (2 octets)</dt>
        <dd>Selects which pre-shared key the sender used.
        Allows the receiver to maintain multiple keys
        simultaneously for rotation purposes.  Network byte order.</dd>
        <dt>Time Step (4 octets)</dt>
        <dd>A coarse, monotonically increasing counter
        used for replay prevention.  The granularity
        is implementation-defined; a step of 30 seconds
        is RECOMMENDED.  Network byte order.</dd>
        <dt>Authentication Tag (8 octets)</dt>
        <dd>The truncated SipHash-2-4 output as described in <xref target="mac"/>.</dd>
      </dl>
    </section>
    <section title="Hash Input and MAC Computation" anchor="mac">
      <t>The Authentication Tag is computed as follows:</t>
      <figure anchor="spa-input">
        <artwork>
<![CDATA[
struct spa_input {
    uint16_t  exid;       /* ExID, network byte order         */
    uint16_t  ver;        /* Version                           */
    uint16_t  key_id;     /* Key ID, network byte order        */
    uint32_t  time_step;  /* Time Step, network byte order     */
    uint32_t  tcp_seq;    /* TCP ISN from the SYN, net order   */
};

tag = SipHash-2-4(k0, k1, spa_input)
]]>
        </artwork>
      </figure>
      <t>where k0 and k1 are the two 64-bit halves of the 128-bit pre-shared
      key identified by Key ID.</t>
      <t>The TCP ISN (tcp_seq) is included so that the tag is cryptographically
      bound to this specific SYN and cannot be replayed in a different
      connection with a different ISN.</t>
      <t>The source IP address is deliberately excluded from the hash input.
      Including it would break authentication through NAT (see <xref target="nat"/>).</t>
      <t>SipHash-2-4 is defined in <xref target="SIPHASH"/>.  It produces a 64-bit output.
      All 64 bits are used as the Authentication Tag.</t>
    </section>
    <section title="Client Behavior" anchor="client">
      <t>The client MUST:</t>
      <ol>
        <li>Select a Key ID and retrieve the corresponding 128-bit pre-shared
        key.</li>
        <li>Obtain a current Time Step value.</li>
        <li>Generate a normal TCP SYN, allowing the TCP stack to select an
        ISN.</li>
        <li>Intercept the outgoing SYN before transmission (e.g., at the TC
        egress hook).</li>
        <li>Populate the spa_input struct with the chosen ExID, version,
        Key ID, Time Step, and the TCP ISN from the SYN header.</li>
        <li>Compute the Authentication Tag.</li>
        <li>Prepend the tcp_opt_spa option to the existing TCP option list,
        shifting other options forward.</li>
        <li>Update tcphdr.doff to reflect the added option bytes.</li>
        <li>Recompute the TCP checksum and IP total length.</li>
      </ol>
      <t>The client SHOULD check that the resulting TCP header does not exceed
      60 octets (the maximum allowed by the 4-bit doff field).  If the
      option list is already full, the client MAY omit less-critical options
      (e.g., TCP timestamp) to make room, or MAY fail open (send without
      the SPA option) if configured to allow it.</t>
    </section>
    <section title="Server Behavior" anchor="server">
      <t>The server MUST:</t>
      <ol>
        <li>At the earliest possible processing point (XDP is RECOMMENDED),
        inspect all incoming TCP packets.</li>
        <li>For each TCP SYN destined to a protected (IP, port) pair:
          <ol>
            <li>Search the TCP option list for an option with kind 253 and the
            expected ExID.  If absent, DROP the SYN.</li>
            <li>Extract Key ID from the option.</li>
            <li>Look up the pre-shared key by (destination, Key ID).  If not
            found, DROP the SYN.</li>
            <li>Populate spa_input from the option fields and the SYN's ISN.</li>
            <li>Compute the expected tag and compare it to the tag in the
            option.  If they differ, DROP the SYN.</li>
            <li>Validate the Time Step: it MUST be within a configurable
            acceptance window of the server's current time (e.g., +/- 1
            step).  If outside the window, DROP the SYN (replay or
            excessively stale packet).</li>
            <li>Pass the SYN to the normal TCP stack (XDP_PASS or equivalent).</li>
          </ol>
        </li>
        <li>For non-SYN packets (including ACK, data, FIN) destined to
        protected addresses, the server SHOULD pass them without
        re-checking the SPA option.  The SPA option is not present in
        non-SYN segments.</li>
        <li>For TCP SYN+ACK (the server's handshake reply), no SPA option is
        required.</li>
      </ol>
    </section>
    <section title="Replay Prevention" anchor="replay">
      <t>The Time Step field provides coarse replay prevention.  A captured
      SYN with a valid SPA option can be replayed during the acceptance
      window.  To reduce this risk:</t>
      <ul>
        <li>The acceptance window SHOULD be as small as clock skew and network
        delay allow (one step, e.g., 30 seconds, is RECOMMENDED).</li>
        <li>The TCP ISN binds the tag to a specific SYN.  An attacker who
        replays a captured SYN will be replaying an exact packet including
        the ISN; the server's TCP stack will likely reject the duplicate
        SYN through standard ISN validation, even if the SPA tag passes.</li>
        <li>Implementations MAY maintain a short-lived cache of recently seen
        (Key ID, Time Step, ISN) tuples to detect and reject exact
        duplicates within the acceptance window.</li>
      </ul>
    </section>
    <section title="Key Management" anchor="keys">
      <t>This document does not define a key exchange protocol.  Pre-shared
      keys MUST be distributed through an out-of-band secure channel.</t>
      <t>The Key ID field (16 bits) allows up to 65535 distinct keys per
      (destination, server) pair.  This is sufficient for key rotation:
      a new key is distributed before the old one expires, and the server
      accepts both Key IDs during the overlap period.</t>
    </section>
    <section title="NAT Considerations" anchor="nat">
      <t>Standard (port-preserving) NAPT rewrites the source IP address and
      possibly the source TCP port.  This implementation deliberately
      excludes both from the MAC computation, so NAPT does not break
      authentication.</t>
      <t>The TCP ISN is not rewritten by standard NAT.  If a middlebox were
      to rewrite the ISN (ISN randomization on NAT, which some NAT
      implementations do), the MAC would fail.  This is a known limitation.
      Implementations SHOULD document this.</t>
      <t>      The SPA TCP option itself is not rewritten by any known NAT
      implementation; TCP options other than the Timestamp option (<xref target="RFC7323"/>) are generally passed through unmodified.  However, some
      deep-packet-inspection (DPI) middleboxes may strip unknown TCP options.
      In environments where this is a concern, the SPA option MUST be placed
      as the last option before EOL so that stripping it does not corrupt
      other options.</t>
    </section>
    <section title="Security Considerations" anchor="security">
      <t><strong>Authentication strength:</strong>  SipHash-2-4 with a 128-bit key produces a
      64-bit tag.  An attacker who does not know the key has a 2^-64
      probability of forging a valid tag for a given (Key ID, Time Step,
      ISN) tuple.  At line rate this is a negligible risk; at 1 Mpps the
      expected forgery time exceeds 584,000 years.  SipHash was designed
      for exactly this use case (short-input PRF) and has withstood public
      cryptanalysis since 2012 <xref target="SIPHASH"/>.</t>
      <t><strong>Key secrecy:</strong>  The security of this mechanism depends entirely on the
      secrecy of the pre-shared key.  Keys MUST be generated with a
      cryptographically secure random number generator and distributed via
      a confidential, integrity-protected channel.</t>
      <t><strong>Denial-of-service:</strong>  The server-side verification runs at XDP, before
      any socket or kernel TCP state is allocated.  Illegitimate SYNs are
      dropped at the cost of a map lookup and a SipHash computation —
      both are O(1) and extremely fast.  This mechanism is therefore more
      DoS-resistant than approaches that require socket allocation before
      authentication.</t>
      <t><strong>Option stripping:</strong>  An on-path attacker who strips the SPA option from
      a SYN would cause the connection to be dropped by the server (which
      expects the option on protected destinations).  This is a denial-of-
      service capability, not an authentication bypass.  Replay: see
      <xref target="replay"/>.</t>
      <t><strong>Key ID enumeration:</strong>  An attacker can observe which Key IDs are used
      (the field is not encrypted) and attempt brute-force against a
      specific Key ID.  The 64-bit tag size makes this computationally
      infeasible within any realistic time window.</t>
    </section>
    <section title="IANA Considerations" anchor="iana">
      <t>      This document uses TCP option kind 253, which is permanently
      allocated to IANA for experimental use by <xref target="RFC6994"/>.  <xref target="RFC6994"/> defines
      an ExID sub-registry; the ExID value 0x0001 used in the current
      prototype is a placeholder.  A publication-ready version of this
      specification would request a formal ExID assignment from IANA.</t>
      <t>No other IANA actions are required.</t>
    </section>
  </middle>
  <back>
    <references title="Normative References">
      <reference anchor="RFC2385">
        <front>
          <title>Protection of BGP Sessions via the TCP MD5 Signature Option</title>
          <author initials="A." surname="Heffernan"/>
          <date year="1998" month="August"/>
        </front>
        <seriesInfo name="RFC" value="2385"/>
      </reference>
      <reference anchor="RFC5925">
        <front>
          <title>The TCP Authentication Option</title>
          <author initials="J." surname="Touch"/>
          <author initials="A." surname="Mankin"/>
          <author initials="R." surname="Bonica"/>
          <date year="2010" month="June"/>
        </front>
        <seriesInfo name="RFC" value="5925"/>
      </reference>
      <reference anchor="RFC5926">
        <front>
          <title>Cryptographic Algorithms for the TCP Authentication Option (TCP-AO)</title>
          <author initials="G." surname="Lebovitz"/>
          <author initials="E." surname="Rescorla"/>
          <date year="2010" month="June"/>
        </front>
        <seriesInfo name="RFC" value="5926"/>
      </reference>
      <reference anchor="RFC6994">
        <front>
          <title>Shared Use of Experimental TCP Options</title>
          <author initials="J." surname="Touch"/>
          <date year="2013" month="August"/>
        </front>
        <seriesInfo name="RFC" value="6994"/>
      </reference>
      <reference anchor="RFC7323">
        <front>
          <title>TCP Extensions for High Performance</title>
          <author initials="D." surname="Borman"/>
          <author initials="B." surname="Braden"/>
          <author initials="V." surname="Jacobson"/>
          <author initials="R." surname="Scheffenegger"/>
          <date year="2014" month="September"/>
        </front>
        <seriesInfo name="RFC" value="7323"/>
      </reference>
    </references>
    <references title="Informative References">
      <reference anchor="SIPHASH">
        <front>
          <title>SipHash: a fast short-input PRF</title>
          <author initials="J-P." surname="Aumasson"/>
          <author initials="D. J." surname="Bernstein"/>
          <date year="2012"/>
        </front>
        <seriesInfo name="INDOCRYPT" value="2012"/>
        <format type="TXT" target="https://www.aumasson.jp/siphash/siphash.pdf"/>
      </reference>
      <reference anchor="FWKNOP">
        <front>
          <title>Single Packet Authorization with Fwknop</title>
          <author initials="M." surname="Rash"/>
          <date year="2006"/>
        </front>
        <seriesInfo name="USENIX LISA" value="2006"/>
        <format type="TXT" target="https://www.usenix.org/legacy/events/lisa06/tech/rash.html"/>
      </reference>
    </references>
  </back>
</rfc>

