<?xml version="1.0" encoding="utf-8"?>
<rfc version="3"
     xml:lang="en"
     ipr="trust200902"
     submissionType="independent"
     category="exp"
     docName="draft-maurette-hmtftp-06">

  <front>

    <title abbrev="HMTFTP v0.7">
      HMTFTP: HKDF-Derived TFTP with Optional AEAD Protection (v0.7)
    </title>

    <author fullname="A. Maurette"
            initials="A."
            surname="Maurette">
      <organization>IUT R&amp;T Bethune</organization>
      <address>
        <postal>
          <country>France</country>
        </postal>
        <email>contact@c4tz.fr</email>
      </address>
    </author>

    <date year="2026" month="March" day="29"/>

    <area>Applications</area>

    <keyword>hmtftp</keyword>
    <keyword>tftp</keyword>
    <keyword>aead</keyword>
    <keyword>hkdf</keyword>

    <abstract>
      <t>
        HMTFTP is a lightweight UDP file transfer protocol derived from TFTP.
        It preserves the TFTP transfer model (RRQ/WRQ, DATA, ACK, ERROR, and
        OACK option acknowledgment) and replaces text options with binary TLVs.
        HMTFTP defines optional AEAD protection for DATA payloads using
        HKDF-derived keys from a pre-shared key (PSK).
      </t>
      <t>
        This document describes transfer behavior, extension processing rules,
        key derivation, nonce construction, and operational guidance for
        experimental implementations intended to facilitate interoperability.
      </t>
      <t>
        This document requests IANA actions for a UDP service name/port and
        registries for TLV Types and Ciphersuites. Until assignment, TBD1 is
        used as the default UDP port value.
      </t>
    </abstract>

  </front>

  <middle>

    <section title="Introduction">

      <t>
        The Trivial File Transfer Protocol (TFTP) <xref target="RFC1350"/>
        provides a minimal UDP file transfer service but no built-in
        confidentiality or integrity protections.
      </t>

      <t>
        HMTFTP preserves TFTP operational semantics while introducing:
      </t>

      <ul>
        <li><t>a binary TLV negotiation mechanism for RRQ, WRQ, and OACK;</t></li>
        <li><t>explicit extension processing rules (including critical TLVs);</t></li>
        <li><t>optional AEAD protection of DATA payloads.</t></li>
      </ul>

      <t>
        HMTFTP is intended for controlled or managed environments where simple
        TFTP-like behavior remains desirable but optional integrity and
        confidentiality for transferred payloads are required.
      </t>

    </section>

    <section title="Conventions and Requirements Language">

      <t>
        The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
        "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
        "OPTIONAL" in this document are to be interpreted as described in
        BCP 14, <xref target="RFC2119"/> <xref target="RFC8174"/> when, and
        only when, they appear in all capitals, as shown here.
      </t>

      <ul>
        <li><t><strong>PSK</strong>: pre-shared key</t></li>
        <li><t><strong>AEAD</strong>: authenticated encryption with associated data</t></li>
        <li><t><strong>AAD</strong>: additional authenticated data</t></li>
        <li><t><strong>TID</strong>: transfer identifier (UDP endpoint pair per RFC 1350)</t></li>
      </ul>

    </section>

    <section anchor="relationship"
             title="Relationship to TFTP and Compatibility">

      <section title="Semantic Similarity with TFTP">
        <t>
          HMTFTP reuses the TFTP message types and baseline semantics from
          <xref target="RFC1350"/>: RRQ, WRQ, DATA, ACK, ERROR, block numbering,
          stop-and-wait transfer, and end-of-file signaling by a short final
          DATA block.
        </t>
        <t>
          HMTFTP also reuses the OACK concept from <xref target="RFC2347"/>.
        </t>
      </section>

      <section title="Deployment and Operational Differences">
        <t>
          Compared to baseline TFTP, HMTFTP differs as follows:
        </t>
        <ul>
          <li><t>TLVs are binary and carried in RRQ, WRQ, and OACK.</t></li>
          <li><t>The default server port is TBD1 (configurable), not UDP/69.</t></li>
          <li><t>Optional AEAD protection MAY be negotiated for DATA payloads.</t></li>
        </ul>
      </section>

      <section title="Limits of Direct Interoperability">
        <t>
          Classic TFTP endpoints that do not understand HMTFTP TLVs will not
          reliably interoperate when TLVs are present. In particular, security
          negotiation (ENC_REQ/CNONCE/SNONCE/CIPHER) is HMTFTP-specific.
        </t>
        <t>
          A client that requires security mode MUST set ENC_REQ as Critical and
          MUST abort if it is not acknowledged in OACK.
        </t>
        <t>
          Fallback behavior "as TFTP" means message sequencing and transfer
          semantics follow <xref target="RFC1350"/>. It does not imply use of
          UDP/69: deployments can remain TFTP-like while using the configured
          HMTFTP service port.
        </t>
      </section>

    </section>

    <section title="Protocol Overview">

      <t>
        HMTFTP uses TFTP packet types and augments negotiation with TLVs:
      </t>

      <ul>
        <li><t>Client sends RRQ or WRQ with optional TLVs.</t></li>
        <li><t>
          If TLVs are present and request is accepted, server replies with OACK;
          otherwise transfer follows RFC1350 sequencing.
        </t></li>
        <li><t>RRQ flow: client ACK(0), then server DATA(1), client ACK(1), ...</t></li>
        <li><t>WRQ flow: client DATA(1), server ACK(1), ...</t></li>
      </ul>

      <t>
        If AEAD security mode is accepted, DATA payloads are carried as
        Ciphertext || Tag and validated per block number.
      </t>

    </section>

    <section title="Message Formats">

      <t>
        All multi-octet fields are network byte order (big-endian). HMTFTP
        reuses base TFTP wire formats and appends TLVs to RRQ/WRQ/OACK only.
      </t>

      <section title="RRQ and WRQ">
        <t>
          <strong>RRQ/WRQ</strong> =
          OpCode (2) || Filename (N) || 0 || Mode (M) || 0 || optional TLV
          sequence
        </t>
        <t>
          The optional TLV sequence starts immediately after the Mode
          terminating NUL octet and continues to datagram end.
        </t>
      </section>

      <section title="OACK">
        <t>
          <strong>OACK</strong> = OpCode (2) || TLVs
        </t>
        <t>
          If RRQ/WRQ contains one or more TLVs and the request is accepted, the
          server MUST send OACK (possibly with selected/adjusted values).
        </t>
        <t>
          If RRQ/WRQ contains no TLV, the server SHOULD NOT send OACK and
          SHOULD proceed with baseline TFTP exchange.
        </t>
        <t>
          If RRQ/WRQ carries TLVs and the request is accepted, OACK defines
          the negotiation boundary. The OACK TLV sequence MAY be empty when no
          TLV is selected or echoed. An empty OACK indicates that the request
          was accepted but no offered TLV resulted in an echoed or adjusted
          negotiation item.
        </t>
      </section>

      <section title="DATA and ACK">
        <t>
          <strong>DATA</strong> = OpCode (2) || Block (2) || Payload (0..n)
        </t>
        <t>
          <strong>ACK</strong> = OpCode (2) || Block (2)
        </t>
        <t>
          In AEAD mode, Payload is Ciphertext || Tag, where Tag is 16 octets
          for AES-GCM.
        </t>
      </section>

      <section title="ERROR">
        <t>
          <strong>ERROR</strong> = OpCode (2) || ErrorCode (2) ||
          ErrMsg (string) || 0
        </t>
        <t>
          For extension processing failures, endpoints SHOULD use ErrorCode 0
          ("Not defined") with an informative text message.
        </t>
      </section>

    </section>

    <section anchor="tlv-model"
             title="TLV Encoding, Definitions, and Negotiation">

      <section title="TLV Format and Processing Rules">

        <table anchor="tlv-format-table">
          <name>TLV Format</name>
          <thead>
            <tr>
              <th>Field</th>
              <th>Size</th>
              <th>Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>Type</td>
              <td>16 bits</td>
              <td>MSB is Critical bit; lower 15 bits are TLV code</td>
            </tr>
            <tr>
              <td>Length</td>
              <td>16 bits</td>
              <td>Length of Value in octets</td>
            </tr>
            <tr>
              <td>Value</td>
              <td>variable</td>
              <td>Type-specific data</td>
            </tr>
          </tbody>
        </table>

        <t>
          TLVs MAY appear only in RRQ, WRQ, and OACK.
        </t>

        <t>
          Processing rules:
        </t>

        <ul>
          <li><t>Unknown TLVs with Critical=0 MUST be ignored.</t></li>
          <li><t>Unknown TLVs with Critical=1 MUST cause rejection with ERROR.</t></li>
          <li><t>
            A TLV MUST NOT be duplicated unless its definition explicitly
            permits repetition.
          </t></li>
          <li><t>Malformed TLVs (length mismatch, truncation) MUST be rejected.</t></li>
        </ul>

      </section>

      <section anchor="defined-tlvs" title="Defined TLVs">

        <table anchor="defined-tlvs-table">
          <name>Defined TLVs</name>
          <thead>
            <tr>
              <th>Code</th>
              <th>Name</th>
              <th>Length</th>
              <th>Description</th>
            </tr>
          </thead>
          <tbody>
            <tr><td>0x0001</td><td>BLKSIZE</td><td>2</td><td>Maximum DATA plaintext payload size (uint16)</td></tr>
            <tr><td>0x0002</td><td>TIMEOUT</td><td>2</td><td>Requested retransmission timeout in seconds (uint16)</td></tr>
            <tr><td>0x0003</td><td>TSIZE</td><td>8</td><td>Transfer size in octets (uint64)</td></tr>
            <tr><td>0x0010</td><td>ENC_REQ</td><td>0</td><td>Request AEAD protection for DATA payloads</td></tr>
            <tr><td>0x0011</td><td>CIPHER</td><td>2</td><td>Selected ciphersuite (uint16)</td></tr>
            <tr><td>0x0012</td><td>CNONCE</td><td>16</td><td>Client nonce (16 octets from CSPRNG)</td></tr>
            <tr><td>0x0013</td><td>SNONCE</td><td>16</td><td>Server nonce (16 octets from CSPRNG)</td></tr>
          </tbody>
        </table>

        <t>
          BLKSIZE in OACK MUST be less than or equal to the client offered
          BLKSIZE. TIMEOUT in OACK MUST be an acceptable value to both peers.
        </t>

        <t>
          For TSIZE, RRQ MAY send TSIZE=0 to request file size; WRQ SHOULD send
          known size when available.
        </t>

      </section>

      <section anchor="security-negotiation"
               title="Security Negotiation">

        <t>
          Security mode is negotiated by TLVs and is explicitly bound to
          ENC_REQ, CNONCE, SNONCE, and CIPHER:
        </t>

        <ul>
          <li><t>Client requests security by sending ENC_REQ in RRQ/WRQ.</t></li>
          <li><t>When ENC_REQ is present, client MUST include CNONCE and CIPHER.</t></li>
          <li><t>
            If server accepts, OACK MUST include ENC_REQ, SNONCE, and CIPHER.
          </t></li>
          <li><t>
            When echoing ENC_REQ in OACK, server MUST preserve the request
            Critical bit in the TLV Type field (0x8010 -&gt; 0x8010,
            0x0010 -&gt; 0x0010).
          </t></li>
          <li><t>
            CIPHER in OACK MUST equal client CIPHER; mismatch MUST be rejected.
          </t></li>
          <li><t>
            If client marks ENC_REQ as Critical and server does not echo
            ENC_REQ with matching Type (including Critical bit), client MUST
            abort.
          </t></li>
        </ul>

        <t>
          This specification defines ciphersuite 0x0001 as AEAD AES-256-GCM.
        </t>

      </section>

    </section>

    <section anchor="transfer-procedure" title="Transfer Procedure">

      <section title="Client and Server Behavior">
        <ol>
          <li><t>
            Client sends RRQ or WRQ with desired TLVs.
          </t></li>
          <li><t>
            Server validates request, TLVs, and policy; with TLVs present it
            returns OACK with selected values, otherwise it follows RFC1350
            immediate transfer behavior.
          </t></li>
          <li><t>
            RRQ after OACK: client sends ACK(0), then server starts DATA(1).
          </t></li>
          <li><t>
            WRQ after OACK: client starts DATA(1), then server ACKs each block.
          </t></li>
          <li><t>
            Transfer completes when sender transmits a DATA block with payload
            length smaller than negotiated BLKSIZE (plaintext size in AEAD
            mode).
          </t></li>
        </ol>
        <t>
          When TLVs were present in RRQ/WRQ and accepted, OACK is mandatory.
          When no TLV is present, OACK is normally omitted.
        </t>
      </section>

      <section title="Fallback and Abort Conditions">
        <t>
          If no TLVs are present and no OACK is sent, peers follow direct TFTP
          behavior per <xref target="RFC1350"/>.
        </t>
        <t>
          If TLVs are present but server cannot or will not negotiate, server
          MUST reject the request with ERROR; silent fallback to non-negotiated
          transfer in that case is not permitted.
        </t>
        <t>
          Unknown critical TLVs, malformed TLVs, missing required security TLVs,
          and unsupported requested ciphersuite MUST cause request rejection.
        </t>
      </section>

    </section>

    <section anchor="key-derivation" title="Key Derivation">

      <t>
        HMTFTP assumes an externally provisioned PSK. Key derivation uses
        HKDF-SHA-256 as specified in <xref target="RFC5869"/>. In security
        mode:
      </t>

      <ul>
        <li><t>IKM = PSK</t></li>
        <li><t>salt = CNONCE || SNONCE (32 octets)</t></li>
        <li><t>info = "hmtftp keys v1"</t></li>
        <li><t>OKM = HKDF-SHA-256(IKM, salt, info, 44)</t></li>
        <li><t>key = OKM[0..31] (32 octets)</t></li>
        <li><t>iv_base = OKM[32..43] (12 octets)</t></li>
      </ul>

      <t>
        Implementations MUST use the exact info string above for
        interoperability.
      </t>

    </section>

    <section anchor="nonce-construction" title="Nonce Construction">

      <t>
        For DATA block number n, nonce (12 octets) is:
      </t>

      <t>
        nonce = iv_base[0..7] || uint32(n)
      </t>

      <t>
        where uint32(n) is big-endian and n is the 16-bit DATA block number
        widened to 32 bits.
      </t>

      <t>
        Block-number wrap MUST NOT occur within one security context.
        Endpoints MUST terminate transfer before 65536 DATA blocks.
      </t>

    </section>

    <section anchor="aead-rules" title="AEAD Processing Rules">

      <t>
        This section applies only when security mode is successfully negotiated.
      </t>

      <ul>
        <li><t>Algorithm: AEAD AES-256-GCM (<xref target="RFC5116"/>).</t></li>
        <li><t>AAD: 4-octet DATA header (OpCode || Block).</t></li>
        <li><t>Ciphertext length equals plaintext length.</t></li>
        <li><t>Tag length is 16 octets and is appended to ciphertext.</t></li>
        <li><t>
          Retransmission of a DATA block MUST retransmit identical ciphertext
          and tag for that block number.
        </t></li>
        <li><t>
          A valid duplicate DATA(n) (same ciphertext and tag as previously
          accepted for n) MUST be acknowledged with ACK(n) and MUST NOT be
          delivered twice to the application.
        </t></li>
        <li><t>
          If a DATA(n) authenticates but differs from the stored accepted
          ciphertext/tag for the same n, the endpoint MUST abort the transfer.
        </t></li>
        <li><t>
          Duplicate ACK(n) packets MAY occur and MUST be ignored if they do not
          advance sender state.
        </t></li>
      </ul>

      <t>
        On authentication failure, receiver MUST discard the DATA block and
        MUST NOT acknowledge it as valid. The receiver MUST NOT emit per-packet
        authentication error signals.
      </t>

      <t>
        Duplicate packets within the same transfer context are treated as
        retransmissions, not as replays. Packets from other transfer contexts
        (different TID and/or different negotiated key material) MUST NOT be
        accepted as valid for the active transfer.
      </t>

      <t>
        After consecutive authentication failures or retransmission timeout
        exhaustion for a block, endpoint MUST terminate the transfer context.
        It MAY send at most one generic ERROR (ErrorCode 0) before closing.
      </t>

      <t>
        BLKSIZE in security mode refers to plaintext bytes only.
      </t>

    </section>

    <section title="Error Handling">

      <t>
        ERROR packet format and baseline error semantics follow
        <xref target="RFC1350"/>.
      </t>

      <t>
        HMTFTP-specific requirements:
      </t>

      <ul>
        <li><t>
          Unsupported or unknown critical TLV: reject with ERROR and terminate
          request processing.
        </t></li>
        <li><t>Malformed TLV sequence: reject with ERROR.</t></li>
        <li><t>
          Missing required security TLVs under ENC_REQ: reject with ERROR.
        </t></li>
        <li><t>
          Unexpected TID during transfer MUST be handled according to
          <xref target="RFC1350"/> (Unknown transfer ID).
        </t></li>
        <li><t>
          Authentication failures on DATA in AEAD mode MUST be handled as
          specified in <xref target="aead-rules"/>.
        </t></li>
      </ul>

    </section>

    <section anchor="timers" title="Timers and Retransmissions">

      <t>
        Endpoints SHOULD support configurable retransmission timeout (RTO),
        exponential backoff, and a maximum retransmission count.
      </t>

      <t>
        TIMEOUT TLV (if offered and accepted) sets the initial RTO value for
        the transfer.
      </t>

      <t>
        In AEAD mode, retransmissions MUST reuse exactly the same ciphertext and
        tag for a given DATA block.
      </t>

    </section>

    <section anchor="pmtu" title="Datagram Sizing and PMTU">

      <t>
        Implementations SHOULD avoid IP fragmentation and follow UDP usage
        guidance from <xref target="RFC8085"/>.
      </t>

      <t>
        In AEAD mode, effective DATA datagram payload is:
        4 (DATA header) + BLKSIZE + 16 (GCM tag).
      </t>

      <t>
        Senders SHOULD choose BLKSIZE so resulting datagrams fit path MTU and
        SHOULD reduce BLKSIZE when fragmentation or Packet Too Big indications
        are observed.
      </t>

    </section>

    <section anchor="dos" title="Resource Limits and DoS Mitigations">

      <t>
        Servers SHOULD bound pre-authentication work, rate-limit ERROR
        responses, and cap concurrent transfers.
      </t>

      <t>
        Implementations SHOULD enforce limits for:
      </t>

      <ul>
        <li><t>maximum negotiated BLKSIZE;</t></li>
        <li><t>maximum transfer size and duration;</t></li>
        <li><t>maximum outstanding transfer contexts.</t></li>
      </ul>

      <t>
        In untrusted environments, additional address-validation or cookie
        mechanisms may be required by deployment policy.
      </t>

    </section>

    <section title="Use Cases and Applicability">

      <t>
        HMTFTP is intended for environments where TFTP simplicity is desired but
        optional payload protection is needed without deploying heavier secure
        transports.
      </t>

      <ul>
        <li><t>secure PXE or boot workflows in controlled infrastructure;</t></li>
        <li><t>firmware distribution for managed device fleets;</t></li>
        <li><t>embedded systems with constrained software footprint;</t></li>
        <li><t>
          controlled operational networks where full TLS-based file transfer is
          impractical.
        </t></li>
      </ul>

      <t>
        HMTFTP does not protect RRQ/WRQ/OACK metadata and is not a replacement
        for general-purpose secure file transfer protocols on untrusted
        networks.
      </t>

    </section>

    <section title="Implementation Status">

      <t>
        This section is provided in accordance with
        <xref target="RFC7942"/>. RFC Editor: please remove this section
        before publication as an RFC.
      </t>

      <t>
        As of March 29, 2026, one public prototype implementation of HMTFTP is
        available at:
        <eref target="https://github.com/c4tzzz/hmtftp">https://github.com/c4tzzz/hmtftp</eref>.
      </t>

      <t>
        The implementation is written in Python and includes client and server
        components. The README states Python >= 3.12. In this environment, the
        executed interpreter version was Python 3.13.5.
      </t>

      <t>
        Based on the provided source tree (README and code under
        hmtftp/protocol), the implementation covers RRQ/WRQ/OACK/DATA/ACK/ERROR
        processing, TLV negotiation, optional AEAD DATA protection
        (AES-256-GCM), HKDF-SHA-256 key derivation, and automated tests.
      </t>

      <t>
        Repository commit identification:
      </t>
      <sourcecode type="bash" markers="true"><![CDATA[
git clone https://github.com/c4tzzz/hmtftp /tmp/hmtftp-upstream
git -C /tmp/hmtftp-upstream rev-parse HEAD
# output:
# 5f9a894f6ae9f23ab39b3dd0a6db0d044ee65eb8
]]></sourcecode>
      <t>
        The local source snapshot under experimental/hmtftp-main does not
        include .git metadata; therefore, the revision above was collected from
        a fresh clone of the same repository URL.
      </t>
      <t>
        Public revision permalink:
        <eref target="https://github.com/c4tzzz/hmtftp/tree/5f9a894f6ae9f23ab39b3dd0a6db0d044ee65eb8">https://github.com/c4tzzz/hmtftp/tree/5f9a894f6ae9f23ab39b3dd0a6db0d044ee65eb8</eref>.
      </t>

      <t>
        License metadata:
      </t>
      <sourcecode type="bash" markers="true"><![CDATA[
find /tmp/hmtftp-upstream -maxdepth 2 -type f \
  \( -iname 'LICENSE*' -o -iname 'COPYING*' -o -iname 'NOTICE*' \)
# output:
# (no matching files)
]]></sourcecode>
      <t>
        No explicit license file was found in the upstream repository top-level
        tree at the referenced revision. This remains an implementation
        packaging gap, not a wire-protocol issue.
      </t>

      <t>
        Reproducibility commands executed in this environment:
      </t>
      <sourcecode type="bash" markers="true"><![CDATA[
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/pytest -q
# output:
# 25 passed in 1.70s

./scripts/smoke_local.sh
# output:
# smoke test OK
# artifacts in: /tmp/hmtftp-smoke.O8Njj3
# downloaded: /tmp/hmtftp-smoke.O8Njj3/out/hello.txt
#             /tmp/hmtftp-smoke.O8Njj3/out/secure.bin
# uploaded: /tmp/hmtftp-smoke.O8Njj3/server/inbox/local.bin
]]></sourcecode>

      <t>
        AEAD capture command from the README was attempted with non-interactive
        sudo and did not execute in this environment due sudo restrictions:
      </t>
      <sourcecode type="bash" markers="true"><![CDATA[
sudo -n WAIT_SECS=10 ./scripts/capture_aead.sh \
  /tmp/hmtftp-aead-proof.pcap 192.0.2.1 6369 \
  demo-psk-32-bytes-minimum
# or with an IPv6 documentation address:
sudo -n WAIT_SECS=10 ./scripts/capture_aead.sh \
  /tmp/hmtftp-aead-proof.pcap 2001:db8::1 6369 \
  demo-psk-32-bytes-minimum
# output:
# sudo: a password is required
]]></sourcecode>
      <t>
        Packet-capture evidence is therefore not included in this revision.
      </t>

      <t>
        At the time of writing, this document does not claim multiple
        independent interoperable implementations. The available code is
        intended to support experimentation, protocol validation, and
        implementation feedback for the Experimental track.
      </t>

    </section>

    <section title="Experimental Scope and Evaluation Plan (Informative)">

      <t>
        The wire protocol defined in this document remains aligned with the
        stable core behavior already documented in prior drafts. The
        experimental focus for this version is implementation behavior,
        repeatability, and observability.
      </t>

      <section title="Scope of Experimental Evaluation">
        <ul>
          <li><t>interoperability behavior and negotiation outcomes;</t></li>
          <li><t>loss handling and retransmission behavior with stop-and-wait;</t></li>
          <li><t>datagram sizing behavior (BLKSIZE bounds and operational MTU assumptions);</t></li>
          <li><t>security downgrade handling when ENC_REQ is required by policy;</t></li>
          <li><t>AEAD retransmission and duplicate DATA consistency checks.</t></li>
        </ul>
      </section>

      <section title="Available Evidence in Provided Sources">
        <table anchor="exp-evidence-table">
          <name>Experimental Evidence and Current Status</name>
          <thead>
            <tr>
              <th>Topic</th>
              <th>Evidence in README/Code/Tests</th>
              <th>Status</th>
            </tr>
          </thead>
          <tbody>
            <tr><td>Interop diversity</td><td>Single public implementation only (Implementation Status section)</td><td>Not yet demonstrated beyond one implementation</td></tr>
            <tr><td>Loss/retransmission logic</td><td>Exponential RTO and retransmit loops in client.py/server.py; local smoke and integration tests exercise baseline secure and non-secure transfer paths</td><td>Baseline behavior covered; no controlled impairment traces in this revision</td></tr>
            <tr><td>MTU/sizing behavior</td><td>BLKSIZE selection, bounds, and PMTU guidance in config.py, server negotiation, and Section 13</td><td>Negotiation bounds covered; no path-MTU fault traces in this revision</td></tr>
            <tr><td>Downgrade controls</td><td>OACK validation and ENC_REQ handling in tlv.py/client.py; dedicated regression test in tests/test_negotiation.py</td><td>Covered by dedicated unit test</td></tr>
            <tr><td>AEAD retransmissions</td><td>Session payload caches and duplicate-wire consistency checks in session.py, client.py, and server.py; dedicated regression tests in tests/test_session.py</td><td>Covered for duplicate-payload handling; no packet-loss trace archive in this revision</td></tr>
          </tbody>
        </table>
      </section>

      <section title="Measurement Plan">
        <ul>
          <li><t>Use the reproducible procedure in Appendix B as baseline run criteria.</t></li>
          <li><t>Use pytest and smoke scripts for baseline correctness checks.</t></li>
          <li><t>Use packet capture workflow from README to inspect secure DATA exchanges when privileges are available.</t></li>
          <li><t>Future evaluation should add repeatable network impairment scenarios (loss, reordering, duplication) with archived command lines and logs.</t></li>
        </ul>
      </section>

      <section title="Out of Scope for This Draft Revision">
        <t>
          Native post-quantum key exchange in the HMTFTP wire protocol is out
          of scope for this draft revision. Appendix E discusses only
          out-of-band PSK provisioning considerations.
        </t>
      </section>

    </section>

    <section anchor="security-considerations"
             title="Security Considerations">

      <t>
        Without security mode, HMTFTP provides no confidentiality or integrity
        beyond UDP checksums, consistent with baseline TFTP.
      </t>

      <t>
        With security mode enabled, only DATA payloads are encrypted and
        authenticated. RRQ/WRQ/OACK metadata (filename, mode, TLVs) remains
        cleartext and observable on path.
      </t>

      <t>
        Nonce reuse with AES-GCM is catastrophic. Implementations MUST enforce
        nonce uniqueness and MUST prevent block-number wrap in one security
        context.
      </t>

      <t>
        Endpoints MUST treat authentication failures as potential active attacks
        or corruption events and MUST terminate transfer state after bounded
        retry limits, without exposing per-packet validity side channels.
      </t>

      <t>
        Clients requiring AEAD protection MUST mark ENC_REQ as Critical and MUST
        abort if negotiation is downgraded or not acknowledged.
      </t>

      <t>
        PSK provisioning, storage, rotation, and access control are out of
        scope for wire protocol definition but are critical to real security.
      </t>

    </section>

    <section anchor="iana" title="IANA Considerations">

      <section title="Service Name and UDP Port">

        <t>
          IANA is requested to assign a service name and UDP port in the
          "Service Name and Transport Protocol Port Number Registry"
          (<xref target="RFC6335"/>).
          Until assignment, this document uses TBD1.
        </t>

        <table anchor="iana-port-table">
          <name>HMTFTP Service Name and Port</name>
          <thead>
            <tr>
              <th>Service Name</th>
              <th>Transport Protocol</th>
              <th>Port Number</th>
              <th>Reference</th>
            </tr>
          </thead>
          <tbody>
            <tr><td>hmtftp</td><td>udp</td><td>TBD1</td><td>This document</td></tr>
          </tbody>
        </table>

      </section>

      <section anchor="iana-tlv" title="HMTFTP TLV Types Registry">

        <t>
          IANA is requested to create "HMTFTP TLV Types" for 15-bit TLV codes
          (0x0000-0x7FFF). The Critical bit (MSB on wire) is not part of the
          registry.
        </t>

        <t>
          Registration policy: Expert Review (<xref target="RFC8126"/>).
        </t>

        <t>
          The range 0x7F00-0x7FFF is reserved for Private Use.
        </t>

        <table anchor="iana-tlv-table">
          <name>Initial TLV Registry Entries</name>
          <thead>
            <tr>
              <th>Code</th>
              <th>Name</th>
              <th>Description</th>
              <th>Reference</th>
            </tr>
          </thead>
          <tbody>
            <tr><td>0x0000</td><td>Reserved</td><td>Reserved for future use</td><td>This document</td></tr>
            <tr><td>0x0001</td><td>BLKSIZE</td><td>Block size in octets (uint16)</td><td>This document</td></tr>
            <tr><td>0x0002</td><td>TIMEOUT</td><td>Retransmission timeout in seconds (uint16)</td><td>This document</td></tr>
            <tr><td>0x0003</td><td>TSIZE</td><td>Transfer size in octets (uint64)</td><td>This document</td></tr>
            <tr><td>0x0010</td><td>ENC_REQ</td><td>Request AEAD protection (empty value)</td><td>This document</td></tr>
            <tr><td>0x0011</td><td>CIPHER</td><td>Select ciphersuite (uint16)</td><td>This document</td></tr>
            <tr><td>0x0012</td><td>CNONCE</td><td>Client nonce (16 octets)</td><td>This document</td></tr>
            <tr><td>0x0013</td><td>SNONCE</td><td>Server nonce (16 octets)</td><td>This document</td></tr>
          </tbody>
        </table>

      </section>

      <section title="Opcode Values">
        <t>
          This document does not define new opcode values and therefore does not
          request creation of an HMTFTP OpCodes registry. Opcode values remain
          as defined by <xref target="RFC1350"/> and <xref target="RFC2347"/>.
        </t>
      </section>

      <section title="HMTFTP Ciphersuites Registry">

        <t>
          IANA is requested to create "HMTFTP Ciphersuites" with Expert Review
          policy (<xref target="RFC8126"/>).
        </t>

        <table anchor="iana-ciphersuite-table">
          <name>Initial Ciphersuite Registry Entries</name>
          <thead>
            <tr>
              <th>Value</th>
              <th>Name</th>
              <th>Description</th>
              <th>Reference</th>
            </tr>
          </thead>
          <tbody>
            <tr><td>0x0000</td><td>Reserved</td><td>Reserved for future use</td><td>This document</td></tr>
            <tr><td>0x0001</td><td>AES-256-GCM</td><td>AEAD AES-256-GCM</td><td>This document</td></tr>
          </tbody>
        </table>

      </section>

    </section>

  </middle>

  <back>

    <references>
      <name>References</name>

    <references title="Normative References">

      <reference anchor="RFC1350">
        <front>
          <title>The TFTP Protocol (Revision 2)</title>
          <author fullname="K. Sollins"/>
          <date year="1992" month="07"/>
        </front>
        <seriesInfo name="RFC" value="1350"/>
      </reference>

      <reference anchor="RFC2119">
        <front>
          <title>Key words for use in RFCs to Indicate Requirement Levels</title>
          <author fullname="S. Bradner"/>
          <date year="1997" month="03"/>
        </front>
        <seriesInfo name="BCP" value="14"/>
        <seriesInfo name="RFC" value="2119"/>
      </reference>

      <reference anchor="RFC8174">
        <front>
          <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
          <author fullname="B. Leiba"/>
          <date year="2017" month="05"/>
        </front>
        <seriesInfo name="BCP" value="14"/>
        <seriesInfo name="RFC" value="8174"/>
      </reference>

      <reference anchor="RFC2347">
        <front>
          <title>TFTP Option Extension</title>
          <author fullname="G. Malkin"/>
          <date year="1998" month="05"/>
        </front>
        <seriesInfo name="RFC" value="2347"/>
      </reference>

      <reference anchor="RFC5116">
        <front>
          <title>An Interface and Algorithms for Authenticated Encryption</title>
          <author fullname="D. McGrew"/>
          <date year="2008" month="01"/>
        </front>
        <seriesInfo name="RFC" value="5116"/>
      </reference>

      <reference anchor="RFC5869">
        <front>
          <title>HMAC-based Extract-and-Expand Key Derivation Function (HKDF)</title>
          <author fullname="H. Krawczyk"/>
          <author fullname="P. Eronen"/>
          <date year="2010" month="05"/>
        </front>
        <seriesInfo name="RFC" value="5869"/>
      </reference>

      <reference anchor="RFC6335">
        <front>
          <title>Internet Assigned Numbers Authority (IANA) Procedures for the Management of the Service Name and Transport Protocol Port Number Registry</title>
          <author fullname="M. Cotton"/>
          <author fullname="B. Leiba"/>
          <author fullname="T. Narten"/>
          <date year="2011" month="08"/>
        </front>
        <seriesInfo name="RFC" value="6335"/>
      </reference>

      <reference anchor="RFC8085">
        <front>
          <title>UDP Usage Guidelines</title>
          <author fullname="L. Eggert"/>
          <author fullname="G. Fairhurst"/>
          <author fullname="G. Shepherd"/>
          <date year="2017" month="03"/>
        </front>
        <seriesInfo name="RFC" value="8085"/>
      </reference>

      <reference anchor="RFC8126">
        <front>
          <title>Guidelines for Writing an IANA Considerations Section in RFCs</title>
          <author fullname="M. Cotton"/>
          <author fullname="B. Leiba"/>
          <author fullname="T. Narten"/>
          <date year="2017" month="06"/>
        </front>
        <seriesInfo name="BCP" value="26"/>
        <seriesInfo name="RFC" value="8126"/>
      </reference>

    </references>

    <references title="Informative References">

      <reference anchor="FIPS203">
        <front>
          <title>Module-Lattice-Based Key-Encapsulation Mechanism Standard</title>
          <author fullname="National Institute of Standards and Technology"/>
          <date year="2024" month="08" day="13"/>
        </front>
        <seriesInfo name="FIPS" value="203"/>
        <seriesInfo name="DOI" value="10.6028/NIST.FIPS.203"/>
      </reference>

      <reference anchor="RFC7942">
        <front>
          <title>Improving Awareness of Running Code: The Implementation Status Section</title>
          <author fullname="C. Bormann"/>
          <date year="2016" month="07"/>
        </front>
        <seriesInfo name="RFC" value="7942"/>
      </reference>

    </references>

    </references>

    <section anchor="appendix-example"
             title="Example Negotiated RRQ Exchange (Informative)">

      <t>
        This appendix illustrates a complete RRQ flow with TLV negotiation and
        AEAD mode enabled. Values are examples for implementer guidance.
      </t>

      <t>
        Request context:
      </t>

      <ul>
        <li><t>Filename: "firmware.bin"</t></li>
        <li><t>Mode: "octet"</t></li>
        <li><t>Requested BLKSIZE: 1024</t></li>
        <li><t>Requested TIMEOUT: 3 seconds</t></li>
        <li><t>Security requested: ENC_REQ (Critical), CIPHER=0x0001</t></li>
        <li><t>CNONCE: 16 octets from CSPRNG</t></li>
      </ul>

      <t>
        Example TLVs in RRQ (Type, Length, Value):
      </t>

      <sourcecode type="text"><![CDATA[
BLKSIZE: 0x0001 0x0002 0x0400
TIMEOUT: 0x0002 0x0002 0x0003
ENC_REQ: 0x8010 0x0000
CIPHER : 0x0011 0x0002 0x0001
CNONCE : 0x0012 0x0010 00112233445566778899aabbccddeeff
]]></sourcecode>

      <t>
        Server accepts request and returns OACK with negotiated values:
      </t>

      <sourcecode type="text"><![CDATA[
BLKSIZE: 0x0001 0x0002 0x0400
TIMEOUT: 0x0002 0x0002 0x0003
ENC_REQ: 0x8010 0x0000
CIPHER : 0x0011 0x0002 0x0001
SNONCE : 0x0013 0x0010 a0a1a2a3a4a5a6a7a8a9aaabacadaeaf
]]></sourcecode>

      <t>
        RRQ transfer sequence then proceeds:
      </t>

      <ol>
        <li><t>Client -&gt; Server: RRQ + TLVs</t></li>
        <li><t>Server -&gt; Client: OACK + TLVs</t></li>
        <li><t>Client -&gt; Server: ACK(0)</t></li>
        <li><t>
          Server -&gt; Client: DATA(1) with ciphertext length 1024 and tag length
          16
        </t></li>
        <li><t>Client -&gt; Server: ACK(1)</t></li>
        <li><t>Server -&gt; Client: DATA(2) ...</t></li>
        <li><t>...</t></li>
        <li><t>
          Server -&gt; Client: final DATA(n) with plaintext length &lt; 1024 (EOF)
        </t></li>
        <li><t>Client -&gt; Server: ACK(n)</t></li>
      </ol>

      <t>
        For DATA block 1, nonce is computed as:
      </t>

      <sourcecode type="text"><![CDATA[
nonce(1) = iv_base[0..7] || 0x00000001
AAD      = DATA header (OpCode=3, Block=1)
]]></sourcecode>

      <t>
        If DATA(1) is retransmitted due to loss, ciphertext and tag are
        retransmitted unchanged and the receiver re-ACKs block 1 without
        delivering duplicate plaintext.
      </t>

    </section>

    <section anchor="appendix-repro"
             title="Reproducible Test Procedure (Informative)">

      <section title="Prerequisites from Provided Sources">
        <ul>
          <li><t>Python >= 3.12 (README prerequisite).</t></li>
          <li><t>pip and requirements.txt dependencies: cryptography>=42.0.0 and pytest>=8.2.0.</t></li>
          <li><t>Optional for capture workflow: tcpdump and tshark (README).</t></li>
        </ul>
      </section>

      <section title="Procedure Commands">
        <sourcecode type="bash" markers="true"><![CDATA[
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/pytest -q
./scripts/smoke_local.sh
# optional capture workflow (requires sudo):
sudo WAIT_SECS=10 ./scripts/capture_aead.sh \
  /tmp/hmtftp-aead-proof.pcap 192.0.2.1 6369 \
  demo-psk-32-bytes-minimum
# or:
sudo WAIT_SECS=10 ./scripts/capture_aead.sh \
  /tmp/hmtftp-aead-proof.pcap 2001:db8::1 6369 \
  demo-psk-32-bytes-minimum
]]></sourcecode>
      </section>

      <section title="Execution Status in This Environment">
        <table anchor="appendix-repro-status">
          <name>Execution Status (March 29, 2026)</name>
          <thead>
            <tr>
              <th>Command</th>
              <th>Result</th>
              <th>Observed Output</th>
            </tr>
          </thead>
          <tbody>
            <tr><td>python3 -m venv .venv</td><td>PASS</td><td>Virtual environment created</td></tr>
            <tr><td>.venv/bin/pip install -r requirements.txt</td><td>PASS</td><td>Dependencies installed</td></tr>
            <tr><td>.venv/bin/pytest -q</td><td>PASS</td><td>25 passed in 1.70s</td></tr>
            <tr><td>./scripts/smoke_local.sh</td><td>PASS</td><td>smoke test OK</td></tr>
            <tr><td>sudo -n WAIT_SECS=10 ./scripts/capture_aead.sh ...</td><td>TODO (not executed)</td><td>sudo: a password is required</td></tr>
          </tbody>
        </table>
      </section>

    </section>

    <section anchor="appendix-consistency"
             title="Crypto / TLV Consistency Notes (Informative)">

      <section title="HKDF and Key Material Split">
        <ul>
          <li><t>IKM = PSK (crypto.py: derive_key_material).</t></li>
          <li><t>salt = CNONCE || SNONCE (crypto.py; constants CNONCE_LEN=16 and SNONCE_LEN=16).</t></li>
          <li><t>info string = hmtftp keys v1 (constants HKDF_INFO).</t></li>
          <li><t>OKM length = 44 octets (constants HKDF_OKM_LEN).</t></li>
          <li><t>key = OKM[0..31] and iv_base = OKM[32..43] (crypto.py).</t></li>
        </ul>
      </section>

      <section title="Nonce and AAD Construction">
        <ul>
          <li><t>Nonce format: iv_base[:8] || uint32(block) in network byte order (crypto.py: build_nonce).</t></li>
          <li><t>AAD format: struct.pack('!HH', OP_DATA, block), i.e., DATA opcode + block number (crypto.py: aad_for_block).</t></li>
        </ul>
      </section>

      <section title="TLV IDs and Lengths from Code">
        <table anchor="appendix-tlv-codes">
          <name>TLV Code and Length Consistency</name>
          <thead>
            <tr>
              <th>TLV</th>
              <th>Code</th>
              <th>Length (octets)</th>
              <th>Source</th>
            </tr>
          </thead>
          <tbody>
            <tr><td>BLKSIZE</td><td>0x0001</td><td>2</td><td>constants.py, tlv.py KNOWN_TLV_LENGTHS</td></tr>
            <tr><td>TIMEOUT</td><td>0x0002</td><td>2</td><td>constants.py, tlv.py KNOWN_TLV_LENGTHS</td></tr>
            <tr><td>TSIZE</td><td>0x0003</td><td>8</td><td>constants.py, tlv.py KNOWN_TLV_LENGTHS</td></tr>
            <tr><td>ENC_REQ</td><td>0x0010</td><td>0</td><td>constants.py, tlv.py KNOWN_TLV_LENGTHS</td></tr>
            <tr><td>CIPHER</td><td>0x0011</td><td>2</td><td>constants.py, tlv.py KNOWN_TLV_LENGTHS</td></tr>
            <tr><td>CNONCE</td><td>0x0012</td><td>16</td><td>constants.py, tlv.py KNOWN_TLV_LENGTHS</td></tr>
            <tr><td>SNONCE</td><td>0x0013</td><td>16</td><td>constants.py, tlv.py KNOWN_TLV_LENGTHS</td></tr>
          </tbody>
        </table>
      </section>

      <section title="Deterministic Vector Executed from Implementation">
        <t>
          The following values were executed from the local implementation and
          match tests/test_crypto.py:
        </t>
        <sourcecode type="text"><![CDATA[
PSK    = 30313233343536373839616263646566
         30313233343536373839616263646566
CNONCE = 00112233445566778899aabbccddeeff
SNONCE = a0a1a2a3a4a5a6a7a8a9aaabacadaeaf
key    = e14c36452ca1954c3929b824ececc63d
         fa5c7e4203c75b98ee16f46a0a852cc6
iv_base= 8a5669a255600f2c7c6ae475
nonce1 = 8a5669a255600f2c00000001
aad1   = 00030001
]]></sourcecode>
      </section>

    </section>

    <section anchor="appendix-conformance"
             title="Conformance Matrix (Informative)">

      <table anchor="appendix-conformance-table">
        <name>Requirement to Code and Test Mapping</name>
        <thead>
          <tr>
            <th>Spec Requirement</th>
            <th>Code Location</th>
            <th>Test ID</th>
          </tr>
        </thead>
        <tbody>
          <tr><td>Unknown critical TLV MUST be rejected</td><td>tlv.py: parse_tlvs</td><td>T1</td></tr>
          <tr><td>Duplicate TLV in one message MUST be rejected</td><td>tlv.py: parse_tlvs</td><td>T2</td></tr>
          <tr><td>Malformed or truncated TLV sequence MUST be rejected</td><td>tlv.py: parse_tlvs</td><td>T3</td></tr>
          <tr><td>SNONCE MUST NOT appear in RRQ or WRQ</td><td>tlv.py: validate_request_tlvs</td><td>T4</td></tr>
          <tr><td>Secure OACK MUST include required security TLVs</td><td>tlv.py: validate_oack_tlvs; server.py: _negotiate</td><td>T5, T6</td></tr>
          <tr><td>HKDF profile MUST derive key and iv_base from PSK and nonces</td><td>crypto.py: derive_key_material</td><td>T7</td></tr>
          <tr><td>Nonce and AAD format MUST follow DATA block binding</td><td>crypto.py: build_nonce; aad_for_block</td><td>T8</td></tr>
          <tr><td>AEAD authentication failures MUST be detected</td><td>crypto.py: AeadCipher.decrypt_block; client.py/server.py bounded retry handling</td><td>T9</td></tr>
          <tr><td>Secure GET and PUT transfer path with OACK negotiation</td><td>client.py: download and upload; server.py: _serve_rrq and _serve_wrq</td><td>T10, T11</td></tr>
          <tr><td>Secure downgrade by missing ENC_REQ in OACK MUST be rejected</td><td>client.py: _apply_oack; tlv.py: validate_oack_tlvs</td><td>T12</td></tr>
          <tr><td>Wire duplicate DATA retransmission consistency checks</td><td>session.py: validate_secure_duplicate_data; client.py/server.py duplicate handling</td><td>T13, T14, T15</td></tr>
        </tbody>
      </table>

      <t>
        Test ID mapping:
      </t>
      <ul>
        <li><t>T1 = tests/test_tlv.py function test_parse_unknown_critical_tlv_rejected</t></li>
        <li><t>T2 = tests/test_tlv.py function test_duplicate_tlv_rejected</t></li>
        <li><t>T3 = tests/test_tlv.py function test_malformed_tlv_rejected</t></li>
        <li><t>T4 = tests/test_tlv.py function test_request_forbidden_snonce_rejected</t></li>
        <li><t>T5 = tests/test_negotiation.py function test_rrq_secure_negotiation_produces_required_oack_tlvs</t></li>
        <li><t>T6 = tests/test_negotiation.py function test_cipher_mismatch_in_oack_rejected</t></li>
        <li><t>T7 = tests/test_crypto.py function test_hkdf_derivation_vector</t></li>
        <li><t>T8 = tests/test_crypto.py function test_nonce_and_aad_construction</t></li>
        <li><t>T9 = tests/test_crypto.py function test_aead_authentication_failure</t></li>
        <li><t>T10 = tests/test_integration_transfer.py function test_secure_download_with_oack</t></li>
        <li><t>T11 = tests/test_integration_transfer.py function test_secure_upload</t></li>
        <li><t>T12 = tests/test_negotiation.py function test_client_rejects_secure_downgrade_when_oack_omits_security_tlvs</t></li>
        <li><t>T13 = tests/test_session.py function test_secure_duplicate_identical_ok</t></li>
        <li><t>T14 = tests/test_session.py function test_secure_duplicate_invalid_dropped</t></li>
        <li><t>T15 = tests/test_session.py function test_secure_duplicate_valid_mismatch_aborts</t></li>
      </ul>

    </section>

    <section anchor="appendix-pq-psk"
             title="Post-Quantum PSK Provisioning (Informative)">

      <t>
        This appendix is informative and does not modify the HMTFTP wire
        protocol. The protocol continues to assume an externally provisioned
        PSK.
      </t>

      <t>
        One experimental approach is to establish shared key material out of
        band using ML-KEM from <xref target="FIPS203"/>, then derive and
        provision a PSK for HMTFTP endpoints.
      </t>

      <ol>
        <li><t>Perform ML-KEM key establishment out of band.</t></li>
        <li><t>Derive a deployment PSK from the established shared secret (policy-specific derivation outside this wire specification).</t></li>
        <li><t>Install the resulting PSK on both peers before HMTFTP transfer.</t></li>
      </ol>

      <t>
        This draft does not specify a tested ML-KEM tooling workflow. The
        appendix is limited to architectural positioning of out-of-band PSK
        provisioning and does not define a deployment procedure.
      </t>

      <t>
        Native post-quantum handshake negotiation inside HMTFTP messages is out
        of scope for this document revision.
      </t>

    </section>

    <section anchor="acknowledgements" numbered="false">
      <name>Acknowledgements</name>
      <t>
        The author thanks David Mercier for helpful discussions and feedback.
      </t>
    </section>

  </back>

</rfc>
