<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.18 (Ruby 3.3.3) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-connolly-cfrg-xwing-kem-04" category="info" consensus="true" submissionType="IRTF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.22.0 -->
  <front>
    <title abbrev="xwing">X-Wing: general-purpose hybrid post-quantum KEM</title>
    <seriesInfo name="Internet-Draft" value="draft-connolly-cfrg-xwing-kem-04"/>
    <author fullname="Deirdre Connolly">
      <organization>SandboxAQ</organization>
      <address>
        <email>durumcrustulum@gmail.com</email>
      </address>
    </author>
    <author fullname="Peter Schwabe">
      <organization>MPI-SP &amp; Radboud University</organization>
      <address>
        <email>peter@cryptojedi.org</email>
      </address>
    </author>
    <author initials="B. E." surname="Westerbaan" fullname="Bas Westerbaan">
      <organization>Cloudflare</organization>
      <address>
        <email>bas@cloudflare.com</email>
      </address>
    </author>
    <date year="2024" month="August" day="13"/>
    <area>IRTF</area>
    <workgroup>Crypto Forum</workgroup>
    <keyword>post quantum</keyword>
    <keyword>kem</keyword>
    <keyword>PQ/T hybrid</keyword>
    <abstract>
      <?line 103?>

<t>This memo defines X-Wing, a general-purpose post-quantum/traditional
hybrid key encapsulation mechanism (PQ/T KEM) built on X25519 and
ML-KEM-768.</t>
    </abstract>
    <note removeInRFC="true">
      <name>About This Document</name>
      <t>
        The latest revision of this draft can be found at <eref target="https://dconnolly.github.io/draft-connolly-cfrg-xwing-kem/draft-connolly-cfrg-xwing-kem.html"/>.
        Status information for this document may be found at <eref target="https://datatracker.ietf.org/doc/draft-connolly-cfrg-xwing-kem/"/>.
      </t>
      <t>
        Discussion of this document takes place on the
        Crypto Forum Research Group mailing list (<eref target="mailto:cfrg@ietf.org"/>),
        which is archived at <eref target="https://mailarchive.ietf.org/arch/search/?email_list=cfrg"/>.
        Subscribe at <eref target="https://www.ietf.org/mailman/listinfo/cfrg/"/>.
      </t>
      <t>Source for this draft and an issue tracker can be found at
        <eref target="https://github.com/dconnolly/draft-connolly-cfrg-xwing-kem"/>.</t>
    </note>
  </front>
  <middle>
    <?line 109?>

<section anchor="intro">
      <name>Introduction</name>
      <section anchor="motivation">
        <name>Motivation</name>
        <t>There are many choices that can be made when specifying a hybrid KEM:
the constituent KEMs; their security levels; the combiner; and the hash
within, to name but a few. Having too many similar options are a burden
to the ecosystem.</t>
        <t>The aim of X-Wing is to provide a concrete, simple choice for
post-quantum hybrid KEM, that should be suitable for the vast majority
of use cases.</t>
      </section>
      <section anchor="goals">
        <name>Design goals</name>
        <t>By making concrete choices, we can simplify and improve many aspects of
X-Wing.</t>
        <ul spacing="normal">
          <li>
            <t>Simplicity of definition. Because all shared secrets and cipher texts
are fixed length, we do not need to encode the length. Using SHA3-256,
we do not need HMAC-based construction. For the concrete choice of
ML-KEM-768, we do not need to mix in its ciphertext, see <xref target="secc"/>.</t>
          </li>
          <li>
            <t>Security analysis. Because ML-KEM-768 already assumes the Quantum Random
Oracle Model (QROM), we do not need to complicate the analysis
of X-Wing by considering stronger models.</t>
          </li>
          <li>
            <t>Performance. Not having to mix in the ML-KEM-768 ciphertext is a nice
performance benefit. Furthermore, by using SHA3-256 in the combiner,
which matches the hashing in ML-KEM-768, this hash can be computed in
one go on platforms where two-way Keccak is available.</t>
          </li>
        </ul>
        <t>We aim for "128 bits" security (NIST PQC level 1). Although at the
moment there is no peer-reviewed evidence that ML-KEM-512 does not reach
this level, we would like to hedge against future cryptanalytic
improvements, and feel ML-KEM-768 provides a comfortable margin.</t>
        <t>We aim for X-Wing to be usable for most applications, including
specifically HPKE <xref target="RFC9180"/>.</t>
      </section>
      <section anchor="not-an-interactive-key-agreement">
        <name>Not an interactive key-agreement</name>
        <t>Traditionally most protocols use a Diffie-Hellman (DH) style
non-interactive key-agreement.  In many cases, a DH key agreement can be
replaced by the interactive key-agreement afforded by a KEM without
change in the protocol flow.  One notable example is TLS <xref target="HYBRID"/>
          <xref target="XYBERTLS"/>.  However, not all uses of DH can be replaced in a
straight-forward manner by a plain KEM.</t>
      </section>
      <section anchor="auth">
        <name>Not an authenticated KEM</name>
        <t>In particular, X-Wing is not, borrowing the language of <xref target="RFC9180"/>, an
<em>authenticated</em> KEM.</t>
      </section>
      <section anchor="comparisons">
        <name>Comparisons</name>
        <section anchor="with-hpke-x25519kyber768draft00">
          <name>With HPKE X25519Kyber768Draft00</name>
          <t>X-Wing is most similar to HPKE's X25519Kyber768Draft00
<xref target="XYBERHPKE"/>. The key differences are:</t>
          <ul spacing="normal">
            <li>
              <t>X-Wing uses the final version of ML-KEM-768.</t>
            </li>
            <li>
              <t>X-Wing hashes the shared secrets, to be usable outside of HPKE.</t>
            </li>
            <li>
              <t>X-Wing has a simpler combiner by flattening DHKEM(X25519) into the
final hash.</t>
            </li>
            <li>
              <t>X-Wing does not hash in the ML-KEM-768 ciphertext.</t>
            </li>
          </ul>
          <t>There is also a different KEM called X25519Kyber768Draft00 <xref target="XYBERTLS"/>
which is used in TLS. This one should not be used outside of TLS, as it
assumes the presence of the TLS transcript to ensure non malleability.</t>
        </section>
        <section anchor="with-generic-combiner">
          <name>With generic combiner</name>
          <t>The generic combiner of <xref target="I-D.ounsworth-cfrg-kem-combiners"/> can be
instantiated with ML-KEM-768 and DHKEM(X25519). That achieves similar
security, but:</t>
          <ul spacing="normal">
            <li>
              <t>X-Wing is more performant, not hashing in the ML-KEM-768 ciphertext,
and flattening the DHKEM construction, with the same level of
security.</t>
            </li>
            <li>
              <t>X-Wing has a fixed 32 byte shared secret, instead of a variable shared
secret.</t>
            </li>
            <li>
              <t>X-Wing does not accept the optional counter and fixedInfo arguments.</t>
            </li>
          </ul>
        </section>
      </section>
    </section>
    <section anchor="requirements-notation">
      <name>Requirements Notation</name>
      <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL
NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
"<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" 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>
      <?line -18?>

</section>
    <section anchor="conventions-and-definitions">
      <name>Conventions and Definitions</name>
      <t>This document is consistent with all terminology defined in
<xref target="I-D.driscoll-pqt-hybrid-terminology"/>.</t>
      <t>The following terms are used throughout this document to describe the
operations, roles, and behaviors of HPKE:</t>
      <ul spacing="normal">
        <li>
          <t><tt>concat(x0, ..., xN)</tt>: returns the concatenation of byte
strings. <tt>concat(0x01, 0x0203, 0x040506) = 0x010203040506</tt>.</t>
        </li>
        <li>
          <t><tt>random(n)</tt>: return a pseudorandom byte string of length <tt>n</tt> bytes produced by
a cryptographically-secure random number generator.</t>
        </li>
      </ul>
    </section>
    <section anchor="base-crypto">
      <name>Cryptographic Dependencies</name>
      <t>X-Wing relies on the following primitives:</t>
      <ul spacing="normal">
        <li>
          <t>ML-KEM-768 post-quantum key-encapsulation mechanism (KEM) <xref target="MLKEM"/>:  </t>
          <ul spacing="normal">
            <li>
              <t><tt>ML-KEM-768.KeyGen()</tt>: Randomized algorithm to generate an
ML-KEM-768 key pair <tt>(pk_M, sk_M)</tt> of an encapsulation key <tt>pk_M</tt>
and decapsulation key <tt>sk_M</tt>.
Note that <tt>ML-KEM-768.KeyGen()</tt> returns the keys in reverse
order of <tt>GenerateKeyPair()</tt> defined below.</t>
            </li>
            <li>
              <t><tt>ML-KEM-768.Encaps(pk_M)</tt>: Randomized algorithm to generate <tt>(ss_M,
ct_M)</tt>, an ephemeral 32 byte shared key <tt>ss_M</tt>, and a fixed-length
encapsulation (ciphertext) of that key <tt>ct_M</tt> for encapsulation key <tt>pk_M</tt>.      </t>
              <t><tt>ML-KEM-768.Encaps(pk_M)</tt> <bcp14>MUST</bcp14> perform the encapsulation key check
of <xref target="MLKEM"/> §7.2 and raise an error if it fails.</t>
            </li>
            <li>
              <t><tt>ML-KEM-768.Decap(ct_M, sk_M)</tt>: Deterministic algorithm using the
decapsulation key <tt>sk_M</tt> to recover the shared key from <tt>ct_M</tt>.</t>
            </li>
          </ul>
          <t>
To generate deterministic test vectors, we also use  </t>
          <ul spacing="normal">
            <li>
              <t><tt>ML-KEM-768.KeyGen_internal(d, z)</tt>: Algorithm to generate an
ML-KEM-768 key pair <tt>(pk_M, sk_M)</tt> of an encapsulation key <tt>pk_M</tt>
and decapsulation key <tt>sk_M</tt>.
Note that <tt>ML-KEM-768.KeyGen()</tt> returns the keys in reverse
order of <tt>GenerateKeyPair()</tt> defined below.
<tt>d</tt> and <tt>z</tt> are both 32 byte strings.</t>
            </li>
            <li>
              <t><tt>ML-KEM-768.Encaps_internal(pk_M, m)</tt>: Algorithm to generate <tt>(ss_M, ct_M)</tt>,
an ephemeral 32 byte shared key <tt>ss_M</tt>, and a fixed-length
encapsulation (ciphertext) of that key <tt>ct_M</tt> for encapsulation key
<tt>pk_M</tt>. <tt>m</tt> is a 32 byte string.      </t>
              <t><tt>ML-KEM-768.Encaps_internal(pk_M)</tt> <bcp14>MUST</bcp14> perform the encapsulation key check
of <xref target="MLKEM"/> §7.2 and raise an error if it fails.</t>
            </li>
          </ul>
        </li>
        <li>
          <t>X25519 elliptic curve Diffie-Hellman key-exchange defined in <xref section="5" sectionFormat="of" target="RFC7748"/>:  </t>
          <ul spacing="normal">
            <li>
              <t><tt>X25519(k,u)</tt>: takes 32 byte strings k and u representing a
Curve25519 scalar and curvepoint respectively, and returns
the 32 byte string representing their scalar multiplication.</t>
            </li>
            <li>
              <t><tt>X25519_BASE</tt>: the 32 byte string representing the standard base point
of Curve25519. In hex
it is given by <tt>0900000000000000000000000000000000000000000000000000000000000000</tt>.</t>
            </li>
          </ul>
        </li>
      </ul>
      <t>Note that 9 is the standard basepoint for X25519, cf <xref section="6.1" sectionFormat="of" target="RFC7748"/>.</t>
      <ul spacing="normal">
        <li>
          <t>Symmetric cryptography.  </t>
          <ul spacing="normal">
            <li>
              <t><tt>SHAKE128(message, outlen)</tt>: The extendable-output function (XOF)
defined in Section 6.2 of <xref target="FIPS202"/>.</t>
            </li>
            <li>
              <t><tt>SHA3-256(message)</tt>: The hash defined in Section 6.1 of <xref target="FIPS202"/>.</t>
            </li>
          </ul>
        </li>
      </ul>
    </section>
    <section anchor="x-wing-construction">
      <name>X-Wing Construction</name>
      <section anchor="encoding">
        <name>Encoding and sizes</name>
        <t>X-Wing encapsulation key, decapsulation key, ciphertexts and shared secrets are all
fixed length byte strings.</t>
        <dl>
          <dt>Decapsulation key (private):</dt>
          <dd>
            <t>32 bytes</t>
          </dd>
          <dt>Encapsulation key (public):</dt>
          <dd>
            <t>1216 bytes</t>
          </dd>
          <dt>Ciphertext:</dt>
          <dd>
            <t>1120 bytes</t>
          </dd>
          <dt>Shared secret:</dt>
          <dd>
            <t>32 bytes</t>
          </dd>
        </dl>
      </section>
      <section anchor="key-generation">
        <name>Key generation</name>
        <t>An X-Wing keypair (decapsulation key, encapsulation key) is generated as
follows.</t>
        <artwork><![CDATA[
def expandDecapsulationKey(sk):
  expanded = SHAKE128(sk, 96)
  (pk_M, sk_M) = ML-KEM-768.KeyGen_internal(expanded[0:32], expanded[32:64])
  sk_X = expanded[64:96]
  pk_X = X25519(sk_X, X25519_BASE)
  return (sk_M, sk_X, pk_M, pk_X)

def GenerateKeyPair():
  sk = random(32)
  (sk_M, sk_X, pk_M, pk_X) = expandDecapsulationKey(sk)
  return sk, concat(pk_M, pk_X)
]]></artwork>
        <t><tt>GenerateKeyPair()</tt> returns the 32 byte secret decapsulation key <tt>sk</tt>
and the 1216 byte encapsulation key <tt>pk</tt>.</t>
        <t>Here and in the balance of the document for clarity we use
the <tt>M</tt> and <tt>X</tt>subscripts for ML-KEM-768 and X25519 components respectively.</t>
        <section anchor="derive-key-pair">
          <name>Key derivation</name>
          <t>For testing, it is convenient to have a deterministic version
of key generation. An X-Wing implementation <bcp14>MAY</bcp14> provide the following
derandomized variant of key generation.</t>
          <artwork><![CDATA[
def GenerateKeyPairDerand(sk):
  sk_M, sk_X, pk_M, pk_X = expandDecapsulationKey(sk)
  return sk, concat(pk_M, pk_X)
]]></artwork>
          <t><tt>seed</tt> must be 32 bytes.</t>
          <t><tt>GenerateKeyPairDerand()</tt> returns the 32 byte secret encapsulation key
<tt>sk</tt> and the 32 byte decapsulation key <tt>pk</tt>.</t>
        </section>
      </section>
      <section anchor="combiner">
        <name>Combiner</name>
        <t>Given 32 byte strings <tt>ss_M</tt>, <tt>ss_X</tt>, <tt>ct_X</tt>, <tt>pk_X</tt>, representing the
ML-KEM-768 shared secret, X25519 shared secret, X25519 ciphertext
(ephemeral public key) and X25519 public key respectively, the 32 byte
combined shared secret is given by:</t>
        <artwork><![CDATA[
def Combiner(ss_M, ss_X, ct_X, pk_X):
  return SHA3-256(concat(
    XWingLabel,
    ss_M,
    ss_X,
    ct_X,
    pk_X
  ))
]]></artwork>
        <t>where XWingLabel is the following 6 byte ASCII string</t>
        <artwork><![CDATA[
XWingLabel = concat(
    "\./",
    "/^\",
)
]]></artwork>
        <t>In hex XWingLabel is given by <tt>5c2e2f2f5e5c</tt>.</t>
      </section>
      <section anchor="encaps">
        <name>Encapsulation</name>
        <t>Given an X-Wing encapsulation key <tt>pk</tt>, encapsulation proceeds as follows.</t>
        <artwork><![CDATA[
def Encapsulate(pk):
  pk_M = pk[0:1184]
  pk_X = pk[1184:1216]
  ek_X = random(32)
  ct_X = X25519(ek_X, X25519_BASE)
  ss_X = X25519(ek_X, pk_X)
  (ss_M, ct_M) = ML-KEM-768.Encaps(pk_M)
  ss = Combiner(ss_M, ss_X, ct_X, pk_X)
  ct = concat(ct_M, ct_X)
  return (ss, ct)
]]></artwork>
        <t><tt>pk</tt> is a 1216 byte X-Wing encapsulation key resulting from <tt>GeneratePublicKey()</tt></t>
        <t><tt>Encapsulate()</tt> returns the 32 byte shared secret <tt>ss</tt> and the 1120 byte
ciphertext <tt>ct</tt>.</t>
        <t>Note that <tt>Encapsulate()</tt> may raise an error if the ML-KEM encapsulation
does not pass the check of <xref target="MLKEM"/> §7.2.</t>
        <section anchor="derandomized">
          <name>Derandomized</name>
          <t>For testing, it is convenient to have a deterministic version
of encapsulation. An X-Wing implementation <bcp14>MAY</bcp14> provide
the following derandomized function.</t>
          <artwork><![CDATA[
def EncapsulateDerand(pk, eseed):
  pk_M = pk[0:1184]
  pk_X = pk[1184:1216]
  ek_X = eseed[32:64]
  ct_X = X25519(ek_X, X25519_BASE)
  ss_X = X25519(ek_X, pk_X)
  (ss_M, ct_M) = ML-KEM-768.EncapsDerand(pk_M, eseed[0:32])
  ss = Combiner(ss_M, ss_X, ct_X, pk_X)
  ct = concat(ct_M, ct_X)
  return (ss, ct)
]]></artwork>
          <t><tt>pk</tt> is a 1216 byte X-Wing encapsulation key resulting from <tt>GeneratePublicKey()</tt>
            <tt>eseed</tt> <bcp14>MUST</bcp14> be 64 bytes.</t>
          <t><tt>EncapsulateDerand()</tt> returns the 32 byte shared secret <tt>ss</tt> and the 1120 byte
ciphertext <tt>ct</tt>.</t>
        </section>
      </section>
      <section anchor="decaps">
        <name>Decapsulation</name>
        <artwork><![CDATA[
def Decapsulate(ct, sk):
  (sk_M, sk_X, pk_M, pk_X) = expandDecapsulationKey(sk)
  ct_M = ct[0:1088]
  ct_X = ct[1088:1120]
  ss_M = ML-KEM-768.Decapsulate(ct_M, sk_M)
  ss_X = X25519(sk_X, ct_X)
  return Combiner(ss_M, ss_X, ct_X, pk_X)
]]></artwork>
        <t><tt>ct</tt> is the 1120 byte ciphertext resulting from <tt>Encapsulate()</tt>
          <tt>sk</tt> is a 32 byte X-Wing decapsulation key resulting from <tt>GenerateKeyPair()</tt></t>
        <t><tt>Decapsulate()</tt> returns the 32 byte shared secret.</t>
        <section anchor="keeping-expanded-decapsulation-key-around">
          <name>Keeping expanded decapsulation key around</name>
          <t>For efficiency, an implementation <bcp14>MAY</bcp14> cache the result of <tt>expandDecapsulationKey</tt>.
This is useful in two cases:</t>
          <ol spacing="normal" type="1"><li>
              <t>If multiple ciphertexts for the same key are decapsulated.</t>
            </li>
            <li>
              <t>If a ciphertext is decapsulated for a key that has just been generated.
This happen on the client-side for TLS.</t>
            </li>
          </ol>
          <t>A typical API pattern to achieve this optimization is to have an
opaque decapsulation key object that hides the cached values.
For instance, such an API could have the following functions.</t>
          <ol spacing="normal" type="1"><li>
              <t><tt>UnpackDecapsulationKey(sk)</tt> takes a decapsulation key, and returns
 an opaque object that contains the expanded decapsulation key.</t>
            </li>
            <li>
              <t><tt>Decapsulate(ct, esk)</tt> takes a ciphertext and an expanded decapsulation key.</t>
            </li>
            <li>
              <t><tt>GenerateKeyPair()</tt> returns an encapsulation key and an expanded
 decapsulation key.</t>
            </li>
            <li>
              <t><tt>PackDecapsulationKey(sk)</tt> takes an expanded decapsulation key,
 and returns the packed decapsulation key.</t>
            </li>
          </ol>
          <t>The expanded decapsulation key could cache more computation,
such as the expanded matrix A in ML-KEM.</t>
          <t>Any such expanded decapsulation key <bcp14>MUST NOT</bcp14> be transmitted between
implementations, as this could break the security analysis of X-Wing.
In particular, the MAL-BIND-K-PK and MAL-BIND-K-CT binding
properties of X-Wing do not hold when transmitting the regular ML-KEM
decapsulation key.</t>
        </section>
      </section>
      <section anchor="use-in-hpke">
        <name>Use in HPKE</name>
        <t>X-Wing satisfies the HPKE KEM interface as follows.</t>
        <t>The <tt>SerializePublicKey</tt>, <tt>SerializePrivateKey</tt>,
and <tt>DeserializePrivateKey</tt> are the identity functions,
as X-Wing keys are fixed-length byte strings, see <xref target="encoding"/>.</t>
        <t><tt>DeriveKeyPair()</tt> is given by</t>
        <artwork><![CDATA[
def DeriveKeyPair(ikm):
  return GenerateKeyPairDerand(SHAKE128(ikm, 96))
]]></artwork>
        <t>where the HPKE private key and public key are the X-Wing decapsulation
key and encapsulation key respectively.</t>
        <t><tt>Encap()</tt> is <tt>Encapsulate()</tt> from <xref target="encaps"/>, where an
ML-KEM encapsulation key check failure causes an HPKE <tt>EncapError</tt>.</t>
        <t><tt>Decap()</tt> is <tt>Decapsulate()</tt> from <xref target="decaps"/>.</t>
        <t>X-Wing is not an authenticated KEM: it does not support <tt>AuthEncap()</tt>
and <tt>AuthDecap()</tt>, see <xref target="auth"/>.</t>
        <t>Nsecret, Nenc, Npk, and Nsk are defined in <xref target="iana"/>.</t>
      </section>
      <section anchor="use-in-tls-13">
        <name>Use in TLS 1.3</name>
        <t>For the client's share, the key_exchange value contains
the X-Wing encapsulation key.</t>
        <t>For the server's share, the key_exchange value contains
the X-Wing ciphertext.</t>
        <t>On ML-KEM encapsulation key check failure, the server <bcp14>MUST</bcp14>
abort with an illegal_parameter alert.</t>
      </section>
    </section>
    <section anchor="secc">
      <name>Security Considerations</name>
      <t>Informally, X-Wing is secure if SHA3 is secure, and either X25519 is
secure, or ML-KEM-768 is secure.</t>
      <t>More precisely, if SHA3-256, SHA3-512, SHAKE-128, and SHAKE-256 may be
modelled as a random oracle, then the IND-CCA security of X-Wing is
bounded by the IND-CCA security of ML-KEM-768, and the gap-CDH security
of Curve25519, see <xref target="PROOF"/>.</t>
      <t>The security of X-Wing relies crucially on the specifics of the
Fujisaki-Okamoto transformation used in ML-KEM-768: the X-Wing
combiner cannot be assumed to be secure, when used with different
KEMs. In particular it is not known to be safe to leave
out the post-quantum ciphertext from the combiner in the general case.</t>
      <section anchor="binding-properties">
        <name>Binding properties</name>
        <t>Some protocols rely on further properties of the KEM.
X-Wing satisfies the binding properties MAL-BIND-K-PK and MAL-BIND-K-CT
(TODO: reference to proof).
This implies <xref target="KSMW"/> X-Wing also satisfies</t>
        <ul spacing="normal">
          <li>
            <t>MAL-BIND-K,CT-PK</t>
          </li>
          <li>
            <t>MAL-BIND-K,PK-CT</t>
          </li>
          <li>
            <t>LEAK-BIND-K-PK</t>
          </li>
          <li>
            <t>LEAK-BIND-K-CT</t>
          </li>
          <li>
            <t>LEAK-BIND-K,CT-PK</t>
          </li>
          <li>
            <t>LEAK-BIND-K,PK-CT</t>
          </li>
          <li>
            <t>HON-BIND-K-PK</t>
          </li>
          <li>
            <t>HON-BIND-K-CT</t>
          </li>
          <li>
            <t>HON-BIND-K,CT-PK</t>
          </li>
          <li>
            <t>HON-BIND-K,PK-CT</t>
          </li>
        </ul>
        <t>In contrast, ML-KEM on its own does not achieve
MAL-BIND-K-PK, MAL-BIND-K-CT, nor MAL-BIND-K,PK-CT. <xref target="SCHMIEG"/></t>
      </section>
    </section>
    <section anchor="iana">
      <name>IANA Considerations</name>
      <t>This document requests/registers a new entry to the "HPKE KEM Identifiers"
registry.</t>
      <dl>
        <dt>Value:</dt>
        <dd>
          <t>TBD (please)</t>
        </dd>
        <dt>KEM:</dt>
        <dd>
          <t>X-Wing</t>
        </dd>
        <dt>Nsecret:</dt>
        <dd>
          <t>32</t>
        </dd>
        <dt>Nenc:</dt>
        <dd>
          <t>1120</t>
        </dd>
        <dt>Npk:</dt>
        <dd>
          <t>1216</t>
        </dd>
        <dt>Nsk:</dt>
        <dd>
          <t>32</t>
        </dd>
        <dt>Auth:</dt>
        <dd>
          <t>no</t>
        </dd>
        <dt>Reference:</dt>
        <dd>
          <t>This document</t>
        </dd>
      </dl>
      <t>Furthermore, this document requests/registers a new entry to the TLS
Named Group (or Supported Group) registry, according to the procedures
in <xref section="6" sectionFormat="of" target="TLSIANA"/>.</t>
      <dl>
        <dt>Value:</dt>
        <dd>
          <t>26287 (please)</t>
        </dd>
        <dt>Description:</dt>
        <dd>
          <t>X-Wing</t>
        </dd>
        <dt>DTLS-OK:</dt>
        <dd>
          <t>Y</t>
        </dd>
        <dt>Recommended:</dt>
        <dd>
          <t>Y</t>
        </dd>
        <dt>Reference:</dt>
        <dd>
          <t>This document</t>
        </dd>
        <dt>Comment:</dt>
        <dd>
          <t>PQ/T hybrid of X25519 and ML-KEM-768</t>
        </dd>
      </dl>
    </section>
  </middle>
  <back>
    <references anchor="sec-combined-references">
      <name>References</name>
      <references anchor="sec-normative-references">
        <name>Normative References</name>
        <reference anchor="RFC2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author fullname="S. Bradner" initials="S." surname="Bradner"/>
            <date month="March" year="1997"/>
            <abstract>
              <t>In many standards track documents several words are used to signify the requirements in the specification. These words are often capitalized. This document defines these words as they should be interpreted in IETF documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>
        <reference anchor="RFC8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author fullname="B. Leiba" initials="B." surname="Leiba"/>
            <date month="May" year="2017"/>
            <abstract>
              <t>RFC 2119 specifies common key words that may be used in protocol specifications. This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the defined special meanings.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>
      </references>
      <references anchor="sec-informative-references">
        <name>Informative References</name>
        <reference anchor="I-D.driscoll-pqt-hybrid-terminology">
          <front>
            <title>Terminology for Post-Quantum Traditional Hybrid Schemes</title>
            <author fullname="Florence D" initials="F." surname="D">
              <organization>UK National Cyber Security Centre</organization>
            </author>
            <date day="7" month="March" year="2023"/>
            <abstract>
              <t>   One aspect of the transition to post-quantum algorithms in
   cryptographic protocols is the development of hybrid schemes that
   incorporate both post-quantum and traditional asymmetric algorithms.
   This document defines terminology for such schemes.  It is intended
   to be used as a reference and, hopefully, to ensure consistency and
   clarity across different protocols, standards, and organisations.

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-driscoll-pqt-hybrid-terminology-02"/>
        </reference>
        <reference anchor="I-D.ounsworth-cfrg-kem-combiners">
          <front>
            <title>Combiner function for hybrid key encapsulation mechanisms (Hybrid KEMs)</title>
            <author fullname="Mike Ounsworth" initials="M." surname="Ounsworth">
              <organization>Entrust Limited</organization>
            </author>
            <author fullname="Aron Wussler" initials="A." surname="Wussler">
              <organization>Proton AG</organization>
            </author>
            <author fullname="Stavros Kousidis" initials="S." surname="Kousidis">
              <organization>BSI</organization>
            </author>
            <date day="31" month="January" year="2024"/>
            <abstract>
              <t>   The migration to post-quantum cryptography often calls for performing
   multiple key encapsulations in parallel and then combining their
   outputs to derive a single shared secret.

   This document defines a comprehensible and easy to implement Keccak-
   based KEM combiner to join an arbitrary number of key shares, that is
   compatible with NIST SP 800-56Cr2 [SP800-56C] when viewed as a key
   derivation function.  The combiners defined here are practical split-
   key PRFs and are CCA-secure as long as at least one of the ingredient
   KEMs is.

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-ounsworth-cfrg-kem-combiners-05"/>
        </reference>
        <reference anchor="FIPS202" target="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf">
          <front>
            <title>FIPS 202: SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions</title>
            <author initials="" surname="National Institute of Standards and Technology">
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
        <reference anchor="MLKEM" target="https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.203.pdf">
          <front>
            <title>FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard</title>
            <author initials="" surname="National Institute of Standards and Technology">
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
        <reference anchor="RFC9180">
          <front>
            <title>Hybrid Public Key Encryption</title>
            <author fullname="R. Barnes" initials="R." surname="Barnes"/>
            <author fullname="K. Bhargavan" initials="K." surname="Bhargavan"/>
            <author fullname="B. Lipp" initials="B." surname="Lipp"/>
            <author fullname="C. Wood" initials="C." surname="Wood"/>
            <date month="February" year="2022"/>
            <abstract>
              <t>This document describes a scheme for hybrid public key encryption (HPKE). This scheme provides a variant of public key encryption of arbitrary-sized plaintexts for a recipient public key. It also includes three authenticated variants, including one that authenticates possession of a pre-shared key and two optional ones that authenticate possession of a key encapsulation mechanism (KEM) private key. HPKE works for any combination of an asymmetric KEM, key derivation function (KDF), and authenticated encryption with additional data (AEAD) encryption function. Some authenticated variants may not be supported by all KEMs. We provide instantiations of the scheme using widely used and efficient primitives, such as Elliptic Curve Diffie-Hellman (ECDH) key agreement, HMAC-based key derivation function (HKDF), and SHA2.</t>
              <t>This document is a product of the Crypto Forum Research Group (CFRG) in the IRTF.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="9180"/>
          <seriesInfo name="DOI" value="10.17487/RFC9180"/>
        </reference>
        <reference anchor="RFC7748">
          <front>
            <title>Elliptic Curves for Security</title>
            <author fullname="A. Langley" initials="A." surname="Langley"/>
            <author fullname="M. Hamburg" initials="M." surname="Hamburg"/>
            <author fullname="S. Turner" initials="S." surname="Turner"/>
            <date month="January" year="2016"/>
            <abstract>
              <t>This memo specifies two elliptic curves over prime fields that offer a high level of practical security in cryptographic applications, including Transport Layer Security (TLS). These curves are intended to operate at the ~128-bit and ~224-bit security level, respectively, and are generated deterministically based on a list of required properties.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="7748"/>
          <seriesInfo name="DOI" value="10.17487/RFC7748"/>
        </reference>
        <reference anchor="HYBRID">
          <front>
            <title>Hybrid key exchange in TLS 1.3</title>
            <author fullname="Douglas Stebila" initials="D." surname="Stebila">
              <organization>University of Waterloo</organization>
            </author>
            <author fullname="Scott Fluhrer" initials="S." surname="Fluhrer">
              <organization>Cisco Systems</organization>
            </author>
            <author fullname="Shay Gueron" initials="S." surname="Gueron">
              <organization>University of Haifa and Amazon Web Services</organization>
            </author>
            <date day="12" month="February" year="2020"/>
            <abstract>
              <t>   Hybrid key exchange refers to using multiple key exchange algorithms
   simultaneously and combining the result with the goal of providing
   security even if all but one of the component algorithms is broken.
   It is motivated by transition to post-quantum cryptography.  This
   document provides a construction for hybrid key exchange in the
   Transport Layer Security (TLS) protocol version 1.3.

   Discussion of this work is encouraged to happen on the TLS IETF
   mailing list tls@ietf.org or on the GitHub repository which contains
   the draft: https://github.com/dstebila/draft-stebila-tls-hybrid-
   design.

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-stebila-tls-hybrid-design-03"/>
        </reference>
        <reference anchor="XYBERHPKE">
          <front>
            <title>X25519Kyber768Draft00 hybrid post-quantum KEM for HPKE</title>
            <author fullname="Bas Westerbaan" initials="B." surname="Westerbaan">
              <organization>Cloudflare</organization>
            </author>
            <author fullname="Christopher A. Wood" initials="C. A." surname="Wood">
              <organization>Cloudflare</organization>
            </author>
            <date day="14" month="May" year="2024"/>
            <abstract>
              <t>   This memo defines X25519Kyber768Draft00, a hybrid post-quantum KEM,
   for HPKE (RFC9180).  This KEM does not support the authenticated
   modes of HPKE.

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-westerbaan-cfrg-hpke-xyber768d00-03"/>
        </reference>
        <reference anchor="XYBERTLS">
          <front>
            <title>X25519Kyber768Draft00 hybrid post-quantum key agreement</title>
            <author fullname="Bas Westerbaan" initials="B." surname="Westerbaan">
              <organization>Cloudflare</organization>
            </author>
            <author fullname="Douglas Stebila" initials="D." surname="Stebila">
              <organization>University of Waterloo</organization>
            </author>
            <date day="24" month="September" year="2023"/>
            <abstract>
              <t>   This memo defines X25519Kyber768Draft00, a hybrid post-quantum key
   exchange for TLS 1.3.

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-tls-westerbaan-xyber768d00-03"/>
        </reference>
        <reference anchor="TLSIANA">
          <front>
            <title>IANA Registry Updates for TLS and DTLS</title>
            <author fullname="Joseph A. Salowey" initials="J. A." surname="Salowey">
              <organization>Venafi</organization>
            </author>
            <author fullname="Sean Turner" initials="S." surname="Turner">
              <organization>sn3rd</organization>
            </author>
            <date day="30" month="April" year="2024"/>
            <abstract>
              <t>   This document updates the changes to TLS and DTLS IANA registries
   made in RFC 8447.  It adds a new value "D" for discouraged to the
   recommended column of the selected TLS registries.

   This document updates the following RFCs: 3749, 5077, 4680, 5246,
   5705, 5878, 6520, 7301, and 8447.

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-ietf-tls-rfc8447bis-09"/>
        </reference>
        <reference anchor="SCHMIEG" target="https://eprint.iacr.org/2024/523">
          <front>
            <title>Unbindable Kemmy Schmidt: ML-KEM is neither MAL-BIND-K-CT nor MAL-BIND-K-PK</title>
            <author initials="S." surname="Schmieg">
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
        <reference anchor="KSMW" target="https://eprint.iacr.org/2024/1233">
          <front>
            <title>Binding Security of Implicitly-Rejecting KEMs and Application to BIKE and HQC</title>
            <author initials="J." surname="Kraemer">
              <organization/>
            </author>
            <author initials="P." surname="Struck">
              <organization/>
            </author>
            <author initials="M." surname="Weishaupl">
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
        <reference anchor="PROOF" target="https://eprint.iacr.org/2024/039">
          <front>
            <title>X-Wing: The Hybrid KEM You’ve Been Looking For</title>
            <author initials="M." surname="Barbosa">
              <organization/>
            </author>
            <author initials="D." surname="Connolly">
              <organization/>
            </author>
            <author initials="J." surname="Duarte">
              <organization/>
            </author>
            <author initials="A." surname="Kaiser">
              <organization/>
            </author>
            <author initials="P." surname="Schwabe">
              <organization/>
            </author>
            <author initials="K." surname="Varner">
              <organization/>
            </author>
            <author initials="B. E." surname="Westerbraan">
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
      </references>
    </references>
    <?line 608?>

<section anchor="implementations">
      <name>Implementations</name>
      <ul spacing="normal">
        <li>
          <t>Go  </t>
          <ul spacing="normal">
            <li>
              <t><eref target="https://github.com/cloudflare/circl/pull/471">CIRCL</eref></t>
            </li>
            <li>
              <t><eref target="https://github.com/FiloSottile/mlkem768">Filippo</eref></t>
            </li>
          </ul>
        </li>
        <li>
          <t>Rust  </t>
          <ul spacing="normal">
            <li>
              <t><eref target="https://github.com/rugo/xwing-kem.rs]">xwing-kem.rs</eref>      </t>
              <t>
Note: implements the older <tt>-00</tt> version of this memo at the time of
writing.</t>
            </li>
          </ul>
        </li>
      </ul>
    </section>
    <section anchor="S-spec">
      <name>Machine-readable specification</name>
      <t>For the convenience of implementors, we provide a reference specification
in Python. This is a specification; not production ready code:
it should not be deployed as-is, as it leaks the private key by its runtime.</t>
      <section anchor="xwingpy">
        <name>xwing.py</name>
        <artwork><![CDATA[
# WARNING This is a specification of X-Wing; not a production-ready
# implementation. It is slow and does not run in constant time.

# Requires the CryptoDome for SHAKE, and pytest for testing. To install, run
#
#   pip install pycryptodome pytest

import binascii
import hashlib

import mlkem
import x25519

XWingLabel = br"""
                \./
                /^\
              """.replace(b'\n', b'').replace(b' ', b'')

assert len(XWingLabel) == 6
assert binascii.hexlify(XWingLabel) == b'5c2e2f2f5e5c'

def expandDecapsulationKey(seed):
    expanded = hashlib.shake_128(seed).digest(length=96)
    pkM, skM = mlkem.KeyGen(expanded[0:64], mlkem.params768)
    skX = expanded[64:96]
    pkX = x25519.X(skX, x25519.BASE)
    return skM, skX, pkM, pkX

def GenerateKeyPairDerand(seed):
    assert len(seed) == 32
    skM, skX, pkM, pkX = expandDecapsulationKey(seed)
    return seed, pkM + pkX

def Combiner(ssM, ssX, ctX, pkX):
    return hashlib.sha3_256(
        XWingLabel +
        ssM +
        ssX +
        ctX +
        pkX
    ).digest()

def EncapsulateDerand(pk, eseed):
    assert len(eseed) == 64
    assert len(pk) == 1216
    pkM = pk[0:1184]
    pkX = pk[1184:1216]
    ekX = eseed[32:64]
    ctX = x25519.X(ekX, x25519.BASE)
    ssX = x25519.X(ekX, pkX)
    ctM, ssM = mlkem.Enc(pkM, eseed[0:32], mlkem.params768)
    ss = Combiner(ssM, ssX, ctX, pkX)
    return ss, ctM + ctX

def Decapsulate(ct, sk):
    assert len(ct) == 1120
    assert len(sk) == 32
    ctM = ct[0:1088]
    ctX = ct[1088:1120]
    skM, skX, pkM, pkX = expandDecapsulationKey(sk)
    ssM = mlkem.Dec(skM, ctM, mlkem.params768)
    ssX = x25519.X(skX, ctX)
    return Combiner(ssM, ssX, ctX, pkX)
]]></artwork>
      </section>
      <section anchor="x25519py">
        <name>x25519.py</name>
        <artwork><![CDATA[
# WARNING This is a specification of X25519; not a production-ready
# implementation. It is slow and does not run in constant time.

p = 2**255 - 19
a24 = 121665

BASE = b'\x09' + b'\x00'*31

def decode(bs):
    return sum(bs[i] << 8*i for i in range(32)) % p

def decodeScalar(k):
    bs = list(k)
    bs[0] &= 248
    bs[31] &= 127
    bs[31] |= 64
    return decode(bs)

# See rfc7748 §5.
def X(k, u):
    assert len(k) == 32
    assert len(u) == 32

    k = decodeScalar(k)
    u = decode(u)
    x1, x2, x3, z2, z3, swap = u, 1, u, 0, 1, 0

    for t in range(255, -1, -1):
        kt = (k >> t) & 1
        swap ^= kt
        if swap == 1:
            x3, x2 = x2, x3
            z3, z2 = z2, z3
        swap = kt

        A = x2 + z2
        AA = (A*A) % p
        B = x2 - z2
        BB = (B*B) % p
        E = AA - BB
        C = x3 + z3
        D = x3 - z3
        DA = (D*A) % p
        CB = (C*B) % p
        x3 = DA + CB
        x3 = (x3 * x3) % p
        z3 = DA - CB
        z3 = (x1 * z3 * z3) % p
        x2 = (AA * BB) % p
        z2 = (E * (AA + (a24 * E) % p)) % p

    if swap == 1:
        x3, x2 = x2, x3
        z2, z3 = z3, z2

    ret = (x2 * pow(z2, p-2, p)) % p
    return bytes((ret >> 8*i) & 255 for i in range(32))
]]></artwork>
      </section>
      <section anchor="mlkempy">
        <name>mlkem.py</name>
        <artwork><![CDATA[
# WARNING This is a specification of Kyber; not a production ready
# implementation. It is slow and does not run in constant time.

# Requires the CryptoDome for SHAKE. To install, run
#
#   pip install pycryptodome pytest
from Crypto.Hash import SHAKE128, SHAKE256

import io
import hashlib
import functools
import collections

from math import floor

q = 3329
nBits = 8
zeta = 17
eta2 = 2

n = 2**nBits
inv2 = (q+1)//2 # inverse of 2

params = collections.namedtuple('params', ('k', 'du', 'dv', 'eta1'))

params512  = params(k = 2, du = 10, dv = 4, eta1 = 3)
params768  = params(k = 3, du = 10, dv = 4, eta1 = 2)
params1024 = params(k = 4, du = 11, dv = 5, eta1 = 2)

def smod(x):
    r = x % q
    if r > (q-1)//2:
        r -= q
    return r

# Rounds to nearest integer with ties going up
def Round(x):
    return int(floor(x + 0.5))

def Compress(x, d):
    return Round((2**d / q) * x) % (2**d)

def Decompress(y, d):
    assert 0 <= y and y <= 2**d
    return Round((q / 2**d) * y)

def BitsToWords(bs, w):
    assert len(bs) % w == 0
    return [sum(bs[i+j] * 2**j for j in range(w))
            for i in range(0, len(bs), w)]

def WordsToBits(bs, w):
    return sum([[(b >> i) % 2 for i in range(w)] for b in bs], [])

def Encode(a, w):
    return bytes(BitsToWords(WordsToBits(a, w), 8))

def Decode(a, w):
    return BitsToWords(WordsToBits(a, 8), w)

def brv(x):
    """ Reverses a 7-bit number """
    return int(''.join(reversed(bin(x)[2:].zfill(nBits-1))), 2)

class Poly:
    def __init__(self, cs=None):
        self.cs = (0,)*n if cs is None else tuple(cs)
        assert len(self.cs) == n

    def __add__(self, other):
        return Poly((a+b) % q for a,b in zip(self.cs, other.cs))

    def __neg__(self):
        return Poly(q-a for a in self.cs)
    def __sub__(self, other):
        return self + -other

    def __str__(self):
        return f"Poly({self.cs}"

    def __eq__(self, other):
        return self.cs == other.cs

    def NTT(self):
        cs = list(self.cs)
        layer = n // 2
        zi = 0
        while layer >= 2:
            for offset in range(0, n-layer, 2*layer):
                zi += 1
                z = pow(zeta, brv(zi), q)

                for j in range(offset, offset+layer):
                    t = (z * cs[j + layer]) % q
                    cs[j + layer] = (cs[j] - t) % q
                    cs[j] = (cs[j] + t) % q
            layer //= 2
        return Poly(cs)

    def RefNTT(self):
        # Slower, but simpler, version of the NTT.
        cs = [0]*n
        for i in range(0, n, 2):
            for j in range(n // 2):
                z = pow(zeta, (2*brv(i//2)+1)*j, q)
                cs[i] = (cs[i] + self.cs[2*j] * z) % q
                cs[i+1] = (cs[i+1] + self.cs[2*j+1] * z) % q
        return Poly(cs)

    def InvNTT(self):
        cs = list(self.cs)
        layer = 2
        zi = n//2
        while layer < n:
            for offset in range(0, n-layer, 2*layer):
                zi -= 1
                z = pow(zeta, brv(zi), q)

                for j in range(offset, offset+layer):
                    t = (cs[j+layer] - cs[j]) % q
                    cs[j] = (inv2*(cs[j] + cs[j+layer])) % q
                    cs[j+layer] = (inv2 * z * t) % q
            layer *= 2
        return Poly(cs)

    def MulNTT(self, other):
        """ Computes self o other, the multiplication of self and other
            in the NTT domain. """
        cs = [None]*n
        for i in range(0, n, 2):
            a1 = self.cs[i]
            a2 = self.cs[i+1]
            b1 = other.cs[i]
            b2 = other.cs[i+1]
            z = pow(zeta, 2*brv(i//2)+1, q)
            cs[i] = (a1 * b1 + z * a2 * b2) % q
            cs[i+1] = (a2 * b1 + a1 * b2) % q
        return Poly(cs)

    def Compress(self, d):
        return Poly(Compress(c, d) for c in self.cs)

    def Decompress(self, d):
        return Poly(Decompress(c, d) for c in self.cs)

    def Encode(self, d):
        return Encode(self.cs, d)

def sampleUniform(stream):
    cs = []
    while True:
        b = stream.read(3)
        d1 = b[0] + 256*(b[1] % 16)
        d2 = (b[1] >> 4) + 16*b[2]
        assert d1 + 2**12 * d2 == b[0] + 2**8 * b[1] + 2**16*b[2]
        for d in [d1, d2]:
            if d >= q:
                continue
            cs.append(d)
            if len(cs) == n:
                return Poly(cs)

def CBD(a, eta):
    assert len(a) == 64*eta
    b = WordsToBits(a, 8)
    cs = []
    for i in range(n):
        cs.append((sum(b[:eta]) - sum(b[eta:2*eta])) % q)
        b = b[2*eta:]
    return Poly(cs)

def XOF(seed, j, i):
    h = SHAKE128.new()
    h.update(seed + bytes([j, i]))
    return h

def PRF1(seed, nonce):
    assert len(seed) == 32
    h = SHAKE256.new()
    h.update(seed + bytes([nonce]))
    return h

def PRF2(seed, msg):
    assert len(seed) == 32
    h = SHAKE256.new()
    h.update(seed + msg)
    return h.read(32)

def G(seed):
    h = hashlib.sha3_512(seed).digest()
    return h[:32], h[32:]

def H(msg): return hashlib.sha3_256(msg).digest()

class Vec:
    def __init__(self, ps):
        self.ps = tuple(ps)

    def NTT(self):
        return Vec(p.NTT() for p in self.ps)

    def InvNTT(self):
        return Vec(p.InvNTT() for p in self.ps)

    def DotNTT(self, other):
        """ Computes the dot product <self, other> in NTT domain. """
        return sum((a.MulNTT(b) for a, b in zip(self.ps, other.ps)),
                   Poly())

    def __add__(self, other):
        return Vec(a+b for a,b in zip(self.ps, other.ps))

    def Compress(self, d):
        return Vec(p.Compress(d) for p in self.ps)

    def Decompress(self, d):
        return Vec(p.Decompress(d) for p in self.ps)

    def Encode(self, d):
        return Encode(sum((p.cs for p in self.ps), ()), d)

    def __eq__(self, other):
        return self.ps == other.ps

def EncodeVec(vec, w):
    return Encode(sum([p.cs for p in vec.ps], ()), w)
def DecodeVec(bs, k, w):
    cs = Decode(bs, w)
    return Vec(Poly(cs[n*i:n*(i+1)]) for i in range(k))
def DecodePoly(bs, w):
    return Poly(Decode(bs, w))

class Matrix:
    def __init__(self, cs):
        """ Samples the matrix uniformly from seed rho """
        self.cs = tuple(tuple(row) for row in cs)

    def MulNTT(self, vec):
        """ Computes matrix multiplication A*vec in the NTT domain. """
        return Vec(Vec(row).DotNTT(vec) for row in self.cs)

    def T(self):
        """ Returns transpose of matrix """
        k = len(self.cs)
        return Matrix((self.cs[j][i] for j in range(k))
                      for i in range(k))

def sampleMatrix(rho, k):
    return Matrix([[sampleUniform(XOF(rho, j, i))
            for j in range(k)] for i in range(k)])

def sampleNoise(sigma, eta, offset, k):
    return Vec(CBD(PRF1(sigma, i+offset).read(64*eta), eta)
               for i in range(k))

def constantTimeSelectOnEquality(a, b, ifEq, ifNeq):
    # WARNING! In production code this must be done in a
    # data-independent constant-time manner, which this implementation
    # is not. In fact, many more lines of code in this
    # file are not constant-time.
    return ifEq if a == b else ifNeq

def InnerKeyGen(seed, params):
    assert len(seed) == 32
    rho, sigma = G(seed + bytes([params.k]))
    A = sampleMatrix(rho, params.k)
    s = sampleNoise(sigma, params.eta1, 0, params.k)
    e = sampleNoise(sigma, params.eta1, params.k, params.k)
    sHat = s.NTT()
    eHat = e.NTT()
    tHat = A.MulNTT(sHat) + eHat
    pk = EncodeVec(tHat, 12) + rho
    sk = EncodeVec(sHat, 12)
    return (pk, sk)

def InnerEnc(pk, msg, seed, params):
    assert len(msg) == 32
    tHat = DecodeVec(pk[:-32], params.k, 12)
    if EncodeVec(tHat, 12) != pk[:-32]:
        raise Exception("ML-KEM public key not normalized")
    rho = pk[-32:]
    A = sampleMatrix(rho, params.k)
    r = sampleNoise(seed, params.eta1, 0, params.k)
    e1 = sampleNoise(seed, eta2, params.k, params.k)
    e2 = sampleNoise(seed, eta2, 2*params.k, 1).ps[0]
    rHat = r.NTT()
    u = A.T().MulNTT(rHat).InvNTT() + e1
    m = Poly(Decode(msg, 1)).Decompress(1)
    v = tHat.DotNTT(rHat).InvNTT() + e2 + m
    c1 = u.Compress(params.du).Encode(params.du)
    c2 = v.Compress(params.dv).Encode(params.dv)
    return c1 + c2

def InnerDec(sk, ct, params):
    split = params.du * params.k * n // 8
    c1, c2 = ct[:split], ct[split:]
    u = DecodeVec(c1, params.k, params.du).Decompress(params.du)
    v = DecodePoly(c2, params.dv).Decompress(params.dv)
    sHat = DecodeVec(sk, params.k, 12)
    return (v - sHat.DotNTT(u.NTT()).InvNTT()).Compress(1).Encode(1)

def KeyGen(seed, params):
    assert len(seed) == 64
    z = seed[32:]
    pk, sk2 = InnerKeyGen(seed[:32], params)
    h = H(pk)
    return (pk, sk2 + pk + h + z)

def Enc(pk, seed, params):
    assert len(seed) == 32

    K, r = G(seed + H(pk))
    ct = InnerEnc(pk, seed, r, params)
    return (ct, K)

def Dec(sk, ct, params):
    sk2 = sk[:12 * params.k * n//8]
    pk = sk[12 * params.k * n//8 : 24 * params.k * n//8 + 32]
    h = sk[24 * params.k * n//8 + 32 : 24 * params.k * n//8 + 64]
    z = sk[24 * params.k * n//8 + 64 : 24 * params.k * n//8 + 96]
    m2 = InnerDec(sk, ct, params)
    K2, r2 = G(m2 + h)
    ct2 = InnerEnc(pk, m2, r2, params)
    return constantTimeSelectOnEquality(
        ct2, ct,
        K2,                 # if ct == ct2
        PRF2(z, ct),        # if ct != ct2
    )
]]></artwork>
      </section>
    </section>
    <section anchor="test-vectors-todo-replace-with-test-vectors-that-re-use-ml-kem-x25519-values">
      <name>Test vectors # TODO: replace with test vectors that re-use ML-KEM, X25519 values</name>
      <artwork><![CDATA[
seed     7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26
sk     7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26
pk
  ec367a5c586f3817a98698b5fc4082907a8873909a7e79ce5d18c84d425362c7956aeb61
  0b9b68949107335ba676142aadd93ed27211c57c2d319c805a01204f5c2948158759f063
  27825379ac8428113e59c215a582a50d0c89505390ecf868e9270cf96c4fa0f94793b599
  8b38a1e3c9039bf4147695c08413a7e190b5d9a8c22a4759bda51fd11064a21c4dadc3bc
  28510697f3a442205214a6cd3f54a9ba45a309437d372654e2c6226e8640810128597abe
  9042932be6af1eb71a6ef156a9baa4c0c05764a8314fc1565d1825a5eb3604f278bc175b
  0666af85a13d97c650a571564eca080a36727bf76460c81a842895e87c9d4fc9c57fc6b1
  49692eed526fb632cd476232a9f3035b4c96d6a14f8cf92e2735a766c7a168e6034369b6
  c17750afcc483af5654b82439f6b9a136cb4f47986dab4c427327675061d7b130572e207
  1f22339a997cf1e1618133ac8b8acd1d7177943c0d1971c84fc48cce7c4c00b95a9f7741
  4c4c07fb3b0c6d51144d36cc8be4ae9b236f89accdd4336bcff11f4fc997ef13c01bb45d
  4001b1949749ebf14e469788ebdbbeced68ba149ca81aab111d0756f1074b7e60031da43
  7709027c4676edc35318a74b1308a8f2b6aef905668bb031a6403ab7a328ba74b9231866
  e287424b42acd1d69b6eab657f2340f433717e581a048ac9be5196fedc36ec212de48149
  bbec9e07ccc8b1f50293e78e469079a3d3588ae146c1859ced376dc13040c4535f253cb4
  0a61b8be95b8b6606d2f607c1035a23566ade289391829ae61cacd36d247a3a864bab43b
  23198481f10f9a5b25b64cb6314baaa0282c59792fe987687b06cb23b397302962cacb9f
  7327301310c7e66b9f5aab93b0f9ba9b5633a1db72fa637c4f6611ca9117788bb335b80d
  d0c989af6b0d8fc9b5c3707a1d848b220a3002b612c294a004c4b52ad1b4b57619d960a6
  59646622a73de9a55de1191dcf8253b50bb2d6e0bed3ab12c4bb81b2826afec87dabccb5
  6b74bdd4c844005097ac94cafea715a57b6e20b49e49869bfdc8015e37a0b3f942f9467b
  7c749f76c951623340660bbd88c16dfbf5176ca855689bbf7287391935b71eda6ef8bab6
  a2ea6e3095a1f2719d10b205130982942c1bbad0bb6c1901879587ac3a290ff20043010e
  181337eb2a20eda44b24e07f12255bbe78279adc51de276d2e602b72dc1ed7489240ab2c
  4e672b527082e363b0b5f51ffbbb79d724435484ca0c7874aff654d61a254eb7ae420b4d
  0a9958a48144e013972cda7f8adcc7c36206725221a79426e7c798e99cb645198c506194
  c3da36415501ea6bccb377921f0172cf9634232b211d626074020cdec29c4d59248c4056
  88f15d6bc556f72bb01d11ae0b2167d33bb2389a2d6dec911a3513fc680d21a265c3f3b1
  90e983d5bab1ae471802024edfd96a2cd51176261107c29f5050ab52ca7210db8668bb80
  064744cb4236e3ac6df26477c8d80ac9a60ca8796f95c5acd960b2f541027c2378ac1570
  8070acfa528a8473248458cb3cf23108949369009b523a945fc70cf3c3add61c4fbbdba9
  1d74c954682182d30071e71648f1b266ea343ab97547c9a3462969ca911a67667e1cb884
  67942eea1ae5d06ac215e64de876fda67c22f74ffe26ff8b56cf606ff799d4a89bb6cee3
  f79506960abcda4e65d8197e0c992244dae91c21068915647f844f49
eseed
  3cb1eea988004b93103cfb0aeefd2a686e01fa4a58e8a3639ca8a1e3f9ae57e235b8cc87
  3c23dc62b8d260169afa2f75ab916a58d974918835d25e6a435085b2
ct
  b45085dc0c2abecd811415924ade853ae88c8dcf8007e6d79bae036648290472989d6f21
  87bc6d39d0f739d315fc03cd8a373ad8927b0db7d419385c9b867b351815a95e7f0f915e
  7356eacce50d328a572565c538b282dc539e4d4b106ba5add0656efb8bd670a32e89fb64
  2eae8235fdc181b2a3ae21d5f3374ce6955484c4fa9dd0a8e454f73e840fa5085070d107
  89e3cc1f6b4274fad17c041c23a8c512e3be23962de5028f427273f5a53dcf43425e9183
  d304abf22b306fb6add4c89a7b54fa93d50393882bad23e06c58c03cbb765a9d1324be9f
  e7b399b7a0f7486b8b03fe186dc5e9ee9738f48e7ef3127a6db992097263dbc51fb227df
  ab0aae2758d8cfd8573c227e19d245503518ee7f533976236075d50f95b5bd101c670714
  209f264c01e31b80295fea54f42e1c62856042bafbe72e1ef8abe12f58b02e4eb6378bc0
  e13339395b6faf95e2738c509975bc1806d1cbad3e586cfa2ba09b2bde20dfb0aaba2cdb
  583ae33c812109a1095adc697befcbd0be0aafee1e41979be026747c918646d38874320a
  af404f28cda6d6d7a7a5386f487983a69064b8bc1fc0a2998a55bb442cfa9b61581263b3
  3f5ae25c4a1efdd890c3fae4481995eaabf1d4a27addc239b99bb8aefec73a9f9c158190
  26d35d48e11de426f7f113e8fe843db011934c8052300cca9fc870f390648ab47ff54362
  9949c5459fae763871e949a4d2f61caf9f6afcfbc00e5b71f85c791ae04d4db90ed09811
  382a8a2a9707f76cbeaa371eb64d2a8d82e1f65b42e0928e5afa288062ca0b28317c9b36
  b27f14161d84d71db377efc6f0f2d7b57594e8fc432c2dbcbc4f55fc3563894a5be4ad40
  a2aa34ca48db0df5b6d8ae51777bf7c6925a40e651629351e86480594f438ee3a34daa7a
  2581e0f573489e71b23bf76dcf8fd3d9c29ca6bcc699753d54b876adb0c0514ae887e102
  9ef195fc3cddb51d03cb518f8dad5044e2299f601b961fa38da47d1e940b58e864cf5dbe
  85a21dafc40b2355144307d09bd2bf8b1c762e7bd5e27308d903e165ecc6176b74564329
  bf37e1ce9257d113897c0099aaa17937735dd13931c5742f5cceaec475c1886bfef42252
  a7ad66f4d4b925faec8e1a9ce0623a895e9c00c57781e66404311720bb94ff0c019081f9
  b846d72451179308f17d4c7ac324a5bbbb914411840364b9b65f6e189c60ef842c155df1
  f96b84f03521803d3cb7016629b4c8159fb0ad3ce1da5e49ceba56f6881be8432200c86e
  291a4cd3b5ea9001e99b418b9d44a3fa0cedb6acf3feef30df4307480967e765530d6183
  add3a198d796a4535abbd8be92d8c2f9ec4217fd459326f0f090764b57207d4cb108af34
  abf120c182011e66393edf2f446f606acb5b0ad5afb4ea5866e4d4158280885bd0ad4dec
  ed058ced8035afc85d1e03c00b7c23b4e74abe8ba12b86a027064bf88443aadb38c82bc6
  21b6880d3e88f6c3bcb03a015d1cc306f7d575ee778cd1b52902be555b4e02b74cfd310b
  d83ab4c81f97fc12e56f17576740ce2a32fc5145030145cfb97e63e0e41d354274a079d3
  e6fb2e15
ss     555a071a8b7520ae95f8e635de8a5f87dbddcbef900576aad29ecdda5459c15a

seed     badfd6dfaac359a5efbb7bcc4b59d538df9a04302e10c8bc1cbf1a0b3a5120ea
sk     badfd6dfaac359a5efbb7bcc4b59d538df9a04302e10c8bc1cbf1a0b3a5120ea
pk
  8b92541ff9b60f49baf1282229853e3249427656bda5440c6f258df3db3382c2b5674590
  fd713397954b785694807938653603666b103ea8709b002a420a7bee64161015631c6c32
  e7c6a53d525eb44940733667447439d830280e7ba75f501958a09ce8e3b7d0038a7bbc22
  e7025700686662f128ce0771d92a8c8af4cf2a414c11b19c695b234bb447471c67aa5a94
  bad89e192563bb33662fda91d24845f0e3b08eac79d03635fe81610e80bb8bcb92fc05cc
  5a12c7d7a5340a81c031d55a6b81b7469ca14c72cf2634b686cac7060a759700d0188511
  9a557a3a23487a5432dce5932f3a3b0c85972b5caf711c5f21066ff7011743d16a22da06
  b7e8ba22d390746034617b2f47c0c392f926a9665ba89244f5b30a1e9388c2a4749c9b70
  c7ba3a2c18c32481373a35acf8157a561613ddb693040b8f319876a7c165ab53aee1b407
  1d782f84e6cb415565ed4c0c00222e1a5c41bd8485eb40a4053183f2753f1c5243a53662
  4f940029307d5d984934d05ce623066bc29ea6b93a3da99c927218f366ae98e168b30c53
  6dec49f108c9f353073e347137604597053f20a03abd71280d235022a03c9d0a43ee2934
  0845ca51f848056ca98506b91f61b26b9582a0556748a56f36d115a0365da0390b22db09
  d8c1029d3494ce811878e68e56295c38712c16f098f89b5a5953b568e7618af069a63c0f
  39ca70f73a0f2cbb150a64884e2c3d491b5013689741715b02227176d767715060da0439
  f39698283428e4b939f9fbbde627ce1be73c19fb54d082b7399383b5ac44e1205dfae12e
  6672601a4cc89a3a80677447e4c53b127b05c528a0e95cb512eb9dc8799ec00c38e427bf
  5ff94775877b87109c69b0bd367223bfe6a65f8b889eeb7a2ad80ea6001654d350cd6654
  20d34f80d99fef72cb124089331585d6c5bd6d057f8e319352db71878196e6587d2d018f
  1496043bac9fd9106d53c30510b6abefd80d3d6319f1cbb5937b3d808706f888c2c07840
  d3a0c53565603909073660b83fc318c775b34909001054b82f9a0cc8155a3b002a12a414
  bad1a69dc2aa1aa489af7965a16989148467539352dd1794356473a3aca6668b5cd006ab
  972236d7484536ec1029d845fb386c9e4a539e593bfb225f558459e28274fccb0a05ac47
  528b459171c76764a47ed8b461f3124c174aa0c51778299a0a299882a63e88510d9adb47
  9c30b47950974b9556cef689bf5006354c4fe11684a9d057f09c213ea63c32228ce5657f
  336a88bda34fff435d5a682a44312da5bc8d8a6b7bfc1a60fab9ce7a268c00dc040f04d0
  b651adf4e31ad6b36981b7a1ccd666a80b0f89928b81b686ce3c6b18e4a921c0a4f4d26b
  f0cb568db77908424b47d8031bb860c19a1f5eb131e838aa5e628bf29b2dfadc0c6e6c8d
  cb8277b783b61b69a303007f3a49018f3592c505618f3c3aeaf775995c6097671ea0cab7
  633c612a24a144dc81abc28fcc62b2b69a1e93d8b6e93ca81a2b9cd49bc1b5373af699aa
  efac7ba66c110198ad91555b9a46ac19322870322e16ec8cfee384c241bc21e28d5a48af
  52d68f37a8bb0669b7b1630889083f5e6bcd99007183014304553ed8d602a3b12df2b26e
  846c4f1a04eed59a7f1855399a47636186d461695ee4ff1f2558e47c05824395bb484b68
  6c33a4dbc1d068a4423693afc0154b88c241340fa9bf6623011dea34
eseed
  17cda7cfad765f5623474d368ccca8af0007cd9f5e4c849f167a580b14aabdefaee7eef4
  7cb0fca9767be1fda69419dfb927e9df07348b196691abaeb580b32d
ct
  04f8b82b60ce1b47119f9b199c02a0be5b709394e6171c8b272509371681701c4d99bc4b
  ee7f16772bb596271f8c80ed215cf777ce1507785a7506b3cc8a9ee516f0f3a90f4af6cf
  f30115a944dc2856b6ec40ff9678dcfd81563ea5db1a4d269c8f1a7fdfbb9f6b44bdaee6
  a7126a143097e758463ad827298990b34170f655591e640a3b9db75610918534b3741283
  e2a0c50c716a8263e8dda9db348e7bab6d1a249f27fb899d05a3bc1cbb2ab0f7b1a378b1
  803bb83af411cf434fba3cd28bcb19ebedb675111359a3aa8575d3868e945fbada53bc9e
  555a0988820a39728b35930762b28509efc7bfd8d046cca32a9896c8071d56c8a508a26b
  dcd58e64696e15813ab66dabbfedb0d6bfa2078cf98131142765c6f9f54a501da1518a9a
  33206ee0fc7145c3cbca30e8b92354b21cecf6e5aced0ce9909a4204f3b007119dcdb303
  f1ff5a3352b78dff6f42a4ffcabe220938665316044fe533f01000e54f8e03f912b7b526
  3d329cfed70dfcf5059fb795426be342b6a849940469579d0f464aa9bba2dbe170c3532f
  0d8ece7bd71b993ca07c09ad010f4966525bdc43a1e2db0bd1b05aad5f70ded58df621ee
  73503f15037e42a130ab2962fcde2b1f10a518ed3c4cd37d1f1ba5e6f21231f4ff2f3925
  b5536186c57ae4efcbaef6ff59c261f1b61cc759c44863efe438b2ca3ad150adfcd5b7cb
  df15ea8d2d7213d362a6364e558c94454425f5705844b2e0fdaa4965aa082c6b2e0e8256
  ce530752f79697af3fd5fd2c42a5bd64633106be2b26f370253c92e8212e9dfcad3ca10c
  cb69e0c8ebe6607c98bf0c75329949601133e554bf86cfecbc6c2093062655b6be07311c
  e7aaf48d24ea4448c45838af1fdf564c8af1a5948ff26d3e61ab3edf6c89570c78ceda20
  42eeae695143b592313d2c010b0911c796a6c90bba167a3705c5f7ccaab2ff21ed8ca281
  949b5b16f8a09be2ed7736903a85c20cf7f6bd48797a20f5225fe90c9ee10a81f8acfd6a
  81747dbe63753b5e594507e14e0a228ea99d4ae51d8caac0dc2f769989c967ebc80dc44c
  9e2f140ec29eab1d372f2370b0f66779337578b91354140c2998f19028c1e5f7634e94bf
  d470f3fbd9ab3f853093262bd403fbb18c1d28d8ac1b90f7b3e99936868bc0ce192ac338
  cf32baf87aeb9b71de977502c77230d4881ad30998c24b767790f16cc56c41e1781debb5
  c6f9d37b27120a3f225bfdf64ed65807fe988ea2c3001f711070a8354ba92468b115445a
  f1bd8a9942287f4ffccaacaa715c34bc24901687f257808e48d7a2b6c49f4dbe46fc6288
  19f09f89ac553da58e06e03553b30cdd3b715d4367b768da472fc2e734a47ab79ea9e30a
  d475a4de23321f8c6c9551e57629180cdde9923a90add52165195c7e670bd5989558307e
  aa0e6513d0225ba1fa213b319c378649bba7a1ddc146b5528d9230f0bc13c2922565d01c
  e23b57c6058dcc0a3153bdd6c2f0fa5394fb0a506cded4ab50a587a63cec6db3393790e0
  de35c4c309d88a41c91bdf6e0e03200693c9651e469aee6f91c98bea4127ae66312f4ae3
  ea155b67
ss     d99c3cd6cde624a73b2f80d9be695c5ab804a42fcca392bc2fd8504b81b2bf6e

seed     ef58538b8d23f87732ea63b02b4fa0f4873360e2841928cd60dd4cee8cc0d4c9
sk     ef58538b8d23f87732ea63b02b4fa0f4873360e2841928cd60dd4cee8cc0d4c9
pk
  9512abd457939c591d51687e8bc2ac7beace759bccb208b113729902d98543479384ac62
  7ca21424304ad9281119f3460c975b6c3612812c5bd1f2422e3773a5c21c6d3862d6c377
  7ee31822a5718402543cf06476171cbf5895332a444e06c35a4c217d57768574a89dc0a5
  8c8160efd847d0861579c278648b92a863b65c688eb41c1adf453d9494cf14f60c339c0b
  3e51a937d16cc1491314e013d8cacde2bcbf05d5994658579c834be29b5f6a1a601e2455
  2a46822cf99838a57154916415dc54ea3363d751ced5b01160677417c81be3ba67c9b190
  bf29c85567b3051b17d6d75b97b883b043c442da3994910296a44f17a187bb51367fc887
  56baab782a45550b3e1c733bbac4b8a668b0ab586f64450d6a05219047785c1956a08620
  7ac0a636d4bb398abf1649adf0e2069bb7ad063273575ac7947cc25beb768312464874c4
  f2091a3c434d93867943b0a3ebc22962aa2b6d5b284e0133f3309fde0c848c5436dd4772
  b1bcb81793affee19ab76c598d76b851282ac13ccfc7d4c6a56b2c336c70bc87221dc163
  c5518ebb3a5807e918033aacc2a3ad5e555131bb6d61c6b1446882a19c36bf142b7e7375
  a6893799b835a1a755383184c0027dbbfc193e546c9389cc53216280ab9c39323d11d468
  ca255e0af805712932656bba50f92528d435cca903d6981a1c2b4c9cb30cbdb4bd5f1a5e
  d84855ad076a18477f202bc699a412c4471a98d9b5974935a5151ef3167c7f8baccfd943
  2504bc7c6081491887d872c25fa7a6dd80cb3946832e828cf3406a1a0522ab3b63bdfb10
  2d13ac2f193e7e7c82f6ec3804e2220c83bd83eb1bd0d65e175c81e7e176b3f6116d6140
  ac409ba4a30d1ef61e7fa0bb88ac209588c75bfa01aab803f55a8f77dc6eee6c0bf34c2c
  99e9aa846a2a590a757182ca8fd7536c0920e39a4fa3f763ea307f67172bacd6c2bc8937
  856b0eb56959bcfac111f964620081b7d380d3000f27f88a26da2db8f12493f641b09c9f
  65a563ebb913e267b1132ba46734cb111cb230d39f1bface22666b42529a01041bccd132
  bb0852995ccd273c384748a78848c284f5447eecaf9fa6c244977b676a1d2c26a8dc65c7
  974c89482189f4d545fd57348c6b670853cc6b383773925eaf336103aa5da5609b4da3c9
  1ce54d2832974f3c445b5424a6182ca792b76de08e96518abaa94afc51646169301ce497
  22c13b5a1bc2ecac1a2408589531cc371a6064268db2bb8be783790efb00e38037cbc294
  e8820c4a9694785b3a8653320dfa296741b9658c6c7c41c91b0875d205191238b28ca860
  a48231f0733d87254ad5572d11e2955645589f0b19651a943ee20c1c7387b706083e0937
  11eb099f4c32e4f26f4ee9ad3e74aeb4624c42c030cd66ad4087cb0cfa5f33323a286379
  c2090df78b1b04f1cc0cf931f6d6a49bcc6540e4c59373c6ebe64ca38886d75451e2192b
  fd20a7aeb528dcf1437665294099c09b2560d66ca900d33dea091d1c9a346c66731941cf
  57522b6fe33f0a27085e23b72e689990f14622111506dc14e2427db9285fbc82a90f9a23
  510746fd6caaf8418b59d622e16ca1edd1c1a452b48b0ab62a7369bd1722d3413a868b1c
  6ccdf2e301ec1f272ba68a1b5e02d743257be8ba94e9eb0f3ec27ce36306ca7b11ab3568
  86212ddf193c9915762936f787a3bf665fc381c954c3bb0297da7836
eseed
  22a96188d032675c8ac850933c7aff1533b94c834adbb69c6115bad4692d8619f90b0cdf
  8a7b9c264029ac185b70b83f2801f2f4b3f70c593ea3aeeb613a7f1b
ct
  5dae4188bb92ba700d830d8dc6aaf6ef699dcd48ac93c3f5b1e4b0ffc22cf21dfcb51be2
  2532844f061bf3b9393c96dad49050bc9244584675c6919232893288f7218ba84d77f21b
  84cdccc514c147b54372b60bd8ee81c3dc96f0bee2eeadddca79e3d483f7d9e71fed01f5
  d6399ad9d7ede107b859b0f4031ea1376691ce6bfe36d1a211ceee3361035117e12ec81e
  3ab6362cfb7b87a0262279a2efd632a704dcea32fa1036a49e631d6d253ef49be6b46b78
  44b963bc05e7ca76b203594a2fa5f2812659a5e7aa74531128343f086c80cc69f3cd90ef
  4fecf1336f796b0c1c3d15cc54b4b7ee653f4c3b1e3cf97a4c1b223da34e90d6905974ab
  4e544ce2af8fdbefe8d5a3e8479ee07afb4ef546dc86d22e62081a27c14613ec1d284f85
  0e96c4b4a8283f5b307d4f43776f86d9e5a7dbcf3073d468ed6f8cb82f8a0b13947703fd
  15c6911c31379e1d1ccc11560044554a4403cad162a4d86ef22be6a794a240af90e8367a
  ffbe4f84c8a4cd76934c1aea726efbf156108de461a276ac34baabc8bdb33085ce5dd9b6
  d89ef66f06c0590ae7a0cb8fd79a16ee4bdf48aa9788093e6e2f6fa63e837aa80072c2fb
  e5503a22b9cfff87d67b4f7508885966e8b5332299fe95f6e023d813e8e44f79d271eb72
  5687d2dd5e06c0fd34cf5ce9108709960a4d23ef8e8f3d3ba65f5ff8bb57d07c6398ba46
  f7a81eba115fa2132066c96fbd1f7bce0749d1f961516c2d86b6f98f3b6f5d4a0dbb530c
  d7c59f4cad1554dd434bf44be5559614aa1b72ef41b179d667827c458b4196675dd538e2
  88bdaebfbe57f13d65ed6cfcf38b5017dcd38c60a8dec0685944c86535b6a3c8a206bace
  a9d26b4d34cf1d28b4e6aead08f5ba668c0e8bd032ace996f112f7ae5d176ac0bccc2ff8
  b25e15ff7b7350e045d41b499179c1a754184fffaff17fa97025437bf590eb142e472d17
  a331e0a54045928a73ed641ac57f1b88b1392198949e625d0b2c5e7e9d721d02d609eca5
  8a3fd830e6daa97ed59cd5466e356203fbe33e5947bf8ece898ecd2740ded4bb9e563355
  4fbe4a72b5564207099e13937bac1e7d7abfa843e95f6293282db0cd1dcd5cf1f3333d98
  2343bcd6de04e9f6ded75b7f395b97790c564fc6be98ef5e18d9ab26c193d6ea2afb4a57
  71255745b727b8734bc06111b4a1d589d56a176cc5f5ea84d36f0402a9830aa5d7029bc6
  5e3042dc85c66bc8a5c848690f8a847ad4ee837fd8eb01d5b5e29c5781afbd321351073f
  73b2fc7517f87e4a72e0206212ae393a7ce82a8144d4ab3b7eadd2efed21a6913ea298a0
  813d1b26c61a0f93623fa587b12bbacd163a76365023b2fc2b09aeea255dedca9d94b17a
  c0577435cc627d53992fafe73b956443b2ac1b8f9572c8b3f648f4e1b6427f90b45709f6
  c18d87e9e8f4d85e747abd90d7889ba92a27620f2ef6e674ec2f289da1b4e1c9ade96235
  82ff69aaad4f7131382fc15b0f2132ab0df74d769421d1d1677619cff13f1c2097bc6b09
  8a7bbca0111658f0fa64de6b6e1c3c8e03db5971a445992227c825590688d203523f5271
  61137334
ss     3c111a7476821d18c4c05192298a881c46de82a25035e1fbc1ec399cd5a29924

]]></artwork>
    </section>
    <section anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>TODO acknowledge.</t>
    </section>
    <section anchor="change-log">
      <name>Change log</name>
      <ul empty="true">
        <li>
          <t><strong>RFC Editor's Note:</strong> Please remove this section prior to publication of a
final version of this document.</t>
        </li>
      </ul>
      <section anchor="since-draft-connolly-cfrg-xwing-kem-03">
        <name>Since draft-connolly-cfrg-xwing-kem-03</name>
        <ul spacing="normal">
          <li>
            <t>Mandate ML-KEM encapsulation key check, and stipulate effect
on TLS and HPKE integration.</t>
          </li>
          <li>
            <t>Add provisional TLS codepoint.</t>
          </li>
        </ul>
      </section>
      <section anchor="since-draft-connolly-cfrg-xwing-kem-02">
        <name>Since draft-connolly-cfrg-xwing-kem-02</name>
        <ul spacing="normal">
          <li>
            <t>Use seed as private key.</t>
          </li>
          <li>
            <t>Expand on caching decapsulation key values.</t>
          </li>
          <li>
            <t>Expand on binding properties.</t>
          </li>
        </ul>
      </section>
      <section anchor="since-draft-connolly-cfrg-xwing-kem-01">
        <name>Since draft-connolly-cfrg-xwing-kem-01</name>
        <ul spacing="normal">
          <li>
            <t>Add list of implementations.</t>
          </li>
          <li>
            <t>Miscellaneous editorial improvements.</t>
          </li>
          <li>
            <t>Add Python reference specification.</t>
          </li>
          <li>
            <t>Correct definition of <tt>ML-KEM-768.KeyGenDerand(seed)</tt>.</t>
          </li>
        </ul>
      </section>
      <section anchor="since-draft-connolly-cfrg-xwing-kem-00">
        <name>Since draft-connolly-cfrg-xwing-kem-00</name>
        <ul spacing="normal">
          <li>
            <t>A copy of the X25519 public key is now included in the X-Wing
decapsulation (private) key, so that decapsulation does not
require separate access to the X-Wing public key. See #2.</t>
          </li>
        </ul>
      </section>
    </section>
  </back>
  <!-- ##markdown-source:
H4sIAAAAAAAAA+W92ZbcRpame+9PgaJWlyKoiCDmgZXKKk5KsSVSSpFZqVxM
lmgADKSLHu4hHzhIR7XOa/Rd3/V71KOcJznfbwa4wz0iKCkrq3uddZgpMgIw
2LCHf/97m8H99PR0sp6uZ/Z2cOPb0z9P5y9vBy/t3C7N7PRis7xYrGzw6n29
nLYBP69Pf9iY+XpzHnzx4NGNianrpX1zO3j3lucmjVnbl4vl+9vBdN4tJpN2
0czNOR23S9OtT5vFfL6Yzd6fNt3y5al75PS1PT8N08lqU59PV6vpYr5+f8ED
D795+lkQfBSY2WrBvKbz1l5Y/pqvb5wEN2w7XS+WUzPTLw/v3OWfxZKfeOjG
ZL45r+3y9qRlMreDOIzT07A8jZIJw6/sfLVZ3Q7Wy42dMO1kYpbW3B4efbtY
vn65XGwuuHJv+f5ivQg+Wyw35zcmr+17bra3J8Gpk0LQS0G/swT98/Ufbz3t
BTV5Y+cbS+Pg6t6CwK/yxjd2Zc2yeRX8Qe1049xMZ9yQhP5latfd2WL5UtfV
iuuv1uuL1e1bt9RMl6Zv7NnQ7JYu3PId3vpnqybfzaar9afqTH28nK5fbWp6
aQdN3PqgYvTMDCmu1qORt8+e+e7OposP9/Lhu2ev1uezG5OJ2axfLZaSL4MG
QbeZzbzp3LfTZbu0wb3+eXeb1Zr59EezxmBuB0/MvK0X7+780d2zXoTtBlE3
y81qvZltzv/lpa6eNYvzyeUhvrZruwyeNK/emtpe0f+jrx+ePvk6+MfgG8M4
mzb40xy5L1fT9fvxiBfq5l8ap+nvMVHpZDvadI7Z3T17cBb8GXnaZW3M/GAa
d83q8Ob+PO7NGLxD73Y8bG1W/9Js7/glzhfLcx5642zwm8/uxVFU3Z5M5JWj
Gw9P75+1y+mqQa6nFz+sT731njKD8ynCXrx8PzRbbOYrHGD9yqtPXstA9RSY
WKnNZw+/foKr3XYTW5vlS4vNDCYzfzO72NSrsznGePZy8eaWftCVW3rs1uOH
T56e6aczeji7aDvfiYekj3VDXoyaP79zmgRP1mjb4IqobXm+WTvZnCI72waf
m9WrgNuTB+/WoIWpZ/b0qw1DrYPPNvNGLVcfu9635ub+nPb/9mp67Po0s+Dh
fMU0NmsbLLrtwCuNEDy1zSsvIp599CVg+FvX3k0vVu63M/3E2pPr1p5ggot2
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
YPHT5/v34vE9fGHvbq0nhyBy+Ggd7908fHbfyvY8/JJ7b13bKLNm1E+c6ox0
WMeXFTjyad9GT/hn41/pyFuW7TXbXsMBts0atfEvRO7RgW2HI9794S5HDX+x
056GXtvh6L5jOkMSsHIfSPin+VQnWo4gG9ac9097Q/K68sDzdOm/sKFXqwzC
PXCmxPwo2amqlTnUKth9EpDr3jyqn6GE/xZE+aiNSzndDXh3ekzTKL9ZP4t3
1tGTv1ZKIxeIpEE9tuv75s1SunwW9b8d9iCJueM5z1plfPHzfYOHgrZiIj9c
hiadr5jON/bAnM7cmfD2qD0+7MiVuXt2erm7S+blTOvuffF47P5yimT6/Yeb
3J0M4r6UAFxS1IGfz/dizzD5I5dmPbtN18Dsqa/XPuO32/FNd825xvGephGr
7t1+Prl2Od9+9dmR3xYiLk/7kV8Fuze/z+b27ZHv9tXZ5kJfPuIeUGXZZUvP
9ODz473q+ivf+dfffBb1vc8X88Zeltjh9tZ2ZCzwl0d2nV47dNwPfb56+Xcb
WH3tjda70VCI+MN4G+/V/qZl8l0Wxft7lvt9PfMbPq+0zdRnwZ8fudlfuwWn
u6NdM5/k/attrs3xLlaH6d2FLNEncRdjdLqCCfWzoP+jizPd9/B2sYW3i19m
U3t99E0+2M39xfpXxmj/dvv2OEPwu9Ezv1fn14XfUU3hyJz1pKA+7lPOYD/n
vNjmnEzz+OQqwuJcbD8f/RX5rkRCtntlors/6G8JdF7Q23bth2X9K8Kc73DU
8sNd/togJ9lfKLO91BcphIoY7fHfkClfjDLli9W4+qNlvLHNpdrNaD7P9udD
a/p43k/n7fGoBqTOVKR6vevOAfz9YRfLtT8QYg/Ez+Y3p7fnN4/gPMfPjw+D
wevj8TjukSuqYVvqsR1sCwaP3CsjH6j5HHjTE0cuvDP1r5tsPNOY9R/35aBw
+Wqx50K7SpGHEv/3cvHWr4gfXFX/WvKNcK9z634WB3z7zk0e+SVePZK2/tN0
zno80YDjqV2mZ5egy9fo+ld/dKLYfT0ExL+f4XhkFa7HBbDDKXm1HA33yYZE
kg8ystfHlxNl/+cKKxlRw75zdIRJ7ltKf+vZs30SKRrgmjsWcLlauzep55eH
f743/uPFdIULTV+ee6Y05JSXZiOliFB5ouDbTz/xjY99aPVs6tgzrkNpXCeG
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
s8n/C3yVSexLuQAA

-->

</rfc>
