<?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-03" 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-03"/>
    <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="warning-ml-kem-768-has-not-been-standardised">
        <name>Warning: ML-KEM-768 has not been standardised</name>
        <t>X-Wing uses ML-KEM-768, which has not been standardised yet. Thus X-Wing
is not finished, yet, and should not be used, yet.</t>
      </section>
      <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>
            </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>
            </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>
        <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>DeserializePublicKey</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>The argument <tt>ikm</tt> to <tt>DeriveKeyPair()</tt> <bcp14>SHOULD</bcp14> be at least 32 octets in
length.  (This is contrary to <xref target="RFC9180"/> which stipulates it should be
at least Nsk=2432 octets in length.)</t>
        <t><tt>Encap()</tt> is <tt>Encapsulate()</tt> from <xref target="encaps"/>.</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>
      </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>TBD (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>
    <section anchor="todo">
      <name>TODO</name>
      <ul spacing="normal">
        <li>
          <t>Which validation do we want to require?</t>
        </li>
      </ul>
    </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 611?>

<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-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+W92XYcR5aueR9P4cVcpwRQAOjzgEplFSel2BIppcisVC4m
j2jubk6ECERAMXBSq1a/Rt/1Xb/HeZR+kv5+M/cIjwBASVlZPazDTJGAu7kN
e/j3v7eZRxwfH09W09W5PQ1ufXf8l+ns1Wnwys7swpwfX64Xl/OlDc7e14tp
G/Dz6vjHtZmt1hfBlw8f35qYul7YN6fBu7c8N2nMyr6aL96fBtNZN59M2nkz
Mxd03C5Mtzpu5rPZ/Pz8/XHTLV4du0eOX9uL4zCZLNf1xXS5nM5nq/eXPPDo
22efB8HvAnO+nDOv6ay1l5a/ZqtbR8Et205X88XUnOuXR3fv8c98wU88dGsy
W1/UdnE6aZnMaRCHcXoclsdRMmH4pZ0t18vTYLVY2wnTTiZmYc3p8Ojb+eL1
q8V8fcmV+4v3l6t58Pl8sb64NXlt33OzPZ0Ex04KQS8F/c4S9M83f7rzrBfU
5I2drS2Ng+t7CwK/ylvf2qU1i+Ys+KPa6caFmZ5zQxL6t6lddSfzxStdVyuu
n61Wl8vTO3fUTJemb+zJ0OyOLtzxHd75V6sm359Pl6vP1Jn6eDVdna1remkH
Tdz5qGL0zDlSXK5GI2+ePfHdnUznH+/l43dPzlYX57cmE7Nenc0Xki+DBkG3
Pj/3pvPAThftwgb3++fdbVZrZtMPZoXBnAZPzayt5+/u/snds16E7RpRN4v1
crU+X1/82ytdPWnmF5OrQ3xjV3YRPG3O3praXtP/428eHT/9Jvjn4FvDOOs2
+PMMuS+W09X78YiX6ubfGqfpHzBR6WQz2nSG2d07eXgS/AV52kVtzGxvGvfM
cv/m7jzunzN4h97teNjaLP+t2dzxS5zNFxc89MbZ4Lef34+jqDqdTOSVoxuP
jh+ctIvpskGux5c/ro699R4zg4spwp6/ej80m69nSxxgdebVJ69loHoKTCzV
5vNH3zzF1U7dxFZm8cpiM4PJzN6cX67r5ckMYzx5NX9zRz/oyh09dufJo6fP
TvTTCT2cXLad78RD0ie6IS9GzV/cPU6Cpyu0bXBF1La4WK+cbI6RnW2DL8zy
LOD25OG7FWhh6nN7/PWaoVbB5+tZo5bLT1zvG3Nzf477f3s1PXF9mvPg0WzJ
NNYrG8y7zcBLjRA8s82ZFxHPPv4KMPyta++ml0v324l+Yu3JTWtPMMF5u2Yx
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
NsPrsNa246P7zxht99I3brjj4KuHd7/czmjvwn6LTUfja0NPX3z9ZKej0e97
9zfdjC75XsScfJRZAqL9K6pz/8qNzGF06NNlKJMdeR7tCvNo7x1WP8aJ9ob9
e7U//+xey7v75O5V9HLgvH+Ub2HJEZar5R3ols7zLdw7L/YtWLvyYVFqv7Uh
UY8cg0Ehi+WtiX9m4TaZ/13w6vYgn917EBxcKkBaoqB//43LvZNNgic7+5W6
gFlsNjf1++XrzT6oa/9621axyv02m/Pbt4NR+YHHKyMyjN/OWf0dqyZ0TZ4Y
ub/7gIjgANk/9WF0uHYYDDI40qnd+aLt3yLp34BobAtiLCc7hzHy/pS31ORw
72bhPbB+v0yfQLAjxAc8f/z1l+7iX50kgIYLnd1rR9c+Ih1VWvSTuzf69AyH
u5uXP0dQ6Xbj5cvyxL84fkRInbYeWuHzeofH+LLpwh9E/teJf2G0JpFydrmb
iqijP879AYXn9x99e/+rFwfDG8X9x1uwqDvbj1e400wXzfkdONn5nbSIDvtH
P5+eT9HKtQ9zb/50TlZxbu9cnL+2F6zkUAN/u16u+ue3H4exWF7byWL9an5n
p5Ub2p+ZOt0WYjxGktKAqi+Pw/Dl+K2J1eZtXv8iVbCaXvTv6AXBW0KeP/KD
mB4LCmb2WC/T+TPjw3tGffnv6bGu/DziVEPR2u/KbiY0HEnbvsO5heGdTmWg
37xfnamIPXBjs9vkXxxKXW7f9/Xv+uk9xtPJlh73gbO1l+fz9451HE+X/fsM
inavh7cZtjkDrEJouFjPJBIfxpy0Ty59/vIfCOUvd7998ujJH2+a3ZYu+Hma
0UydIN/Tx24qTKB2oXlJIujPx23eT1vrjS3/eoCz6H5awwF7vwR/cvaBgqsK
UI54ee5z+d4dCuy2uwsnOkDoKkPn50fqf/I7uguCy+nlcJmn/MGd1oVr18VE
2btYO3HXLJvpdPhdp2jOp/XmvjPt4Zd3zn0nu/tv9eLWrVub98+HP387uXPl
2p3//re9azx50r9mdVB/8rfZJ0dB/cknh6NrQX9poldWIAZKgw624x8Gn30W
5MO9YTEnZ/adXtDdb1h/Mt7o+2Ty0RMmwz7IzimTXjwn5AKv7ffuuInanbTT
V0j1wOdon/njJ9oucTVqlbGdHIdDj6PjJnn64qi/CbUzF0sHI24L9fX1503U
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
S+MmMQy7R8+mF/ap1fbH17OHP66NPjlXJKzWwfuHP+rvJ/bHfk6bba5/coe9
t1tY/af666Rf/5Earaqf7qOp/YNQBHM8+ka1zQSO3XlA/3HVwxdfrIbz0dvt
sL4bf6TcnTXvjI5luI/5du+anbuvCpl3fjL9B632j3Xi+cZ9SPDeyCdjYWvF
Ir3GkXFfvHXr9zJ7pDn2h6T6g0JuD+aXuZMzHKc9jP6PexzNd3LyeqBp2ta9
aqVDq/4Ax6bNjiX1jbT343bcdx+yv+Kh4YkrA35hVNJYelLju/OX7OjSyl+6
OzAEPaUUSE1dg0t5/Ta8qP1REMVqwyr9SLtNlkOTsaLcMSedfdnqxR8Nclz2
KPi4dkQJR8rpJ72NU5evn58eO6K5lcYwgWl37fT/yZ2Lck+Nwqy+Qyd4+E6f
aowJH9zqj8GPXhRzX/3g3qrRByHcOhysxZ+zOnY099faxGJfvSMh3GgS0bUP
aUf1Zluw8Uceim+PhHYIHXjeH2taeDEvRuaydrbCb4O9qM3hlvNiOL58d0HD
cQR3So4OD8ckK/J9avtTehmi2NUudYjjwjMQrX69pX79zNv14UlPcrZX/ANa
+ZurD7y58sCbHXttlOs38chc/SEtHanas9MlAXy12d5lZJ1h6CXKj66kXPaz
P/ITalbPT91jL9Tfc/djbzfrHcNurnNwrXYkxr0Vv9l04MnY1iy06Guee7OD
F9uxl6+vc6fBn98oXx9pbe2tZKu4w63Qo42wox4Afhsm98eoPrjKnz+yOJxv
FKhIovtI3+edfdeb1PULnZu8Bpdid3qUv85UydtuqfrbvzpyuHtfHjnH3gQN
N+ZwPHKY6m7fi92ZDnOTqX253Zi9wfzc+pdgmatLjS3vzp1ykJNvcl2L4DRw
53r2L3/Kgl5sBMfDN7a6uYfhYOmHj/aQpzf3MJxRvtgo+RpBeLFj6IvYCf5C
6jwbRB7vy/zCtbxW5B8lWdsK1ip2428uaOz9P79zG8srWQbtN01dEeeD+xCY
o/2m/7Rtujm5FDwbfWxv4F9sOR2+S6Q/vzFu4T4+YWGPt994tPl0MP/hD/0B
KGec+lN0VRPXJrVl2ZVx0eZRHqZZFhZ5mJVZYtsiqcuwSrrcdnVTlraOTG5N
05mcK3E+Ifz/Q/q51Lf72SbJC5M1WZl3SRkVpirzqqyzrknDMq7CwpRlkVRh
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
aB86Af/wCLLAREwl8a9s1TFJa4amy8JJDYcNxRgMiQfBF0nHRi/eqIgGLVII
w/q1OWdyJQhwU1DQFY3Bcq0dQIISEXySTjW3OopVCQDz6I6YlIEIGjSGVxLj
lXkiVqgaWR8uJejTy5qFSzlzuJZ2Wok1HXlvXSFvVKJ0G/yqILUAntKXEl4a
1ei7EN1IM7Te+dcmSygwJIkGLdSuUHmTcNQCdXopLRZ4xmRFAKSFZMOjiGlV
i5fTIYSxJTjEiTNRHCjXWVmCQ0GSnnCh0aGZTohjdPq4SAXUKcbN//JC76Hp
jUedYdJZfdTnj/j4s21kS+SVWamKpd5g0W4loaPR7lOr/Bi2iGNVOpQD0wR/
AImyVfhFthlIKqao2lqSpEPJMVHmRyQu9K6N3i1OdTi50mFAU8JHiIhSaaz3
AGwEhYVhImBMXwcr4nSyeW/7bqOvUT237Sv3jXuTid7cJv0drvpvgLjvv/P3
fP5qMvlDcPv2t5/fDx6209Vc3xDsvrXv9u3gG/c1i8HCXszf9J/as+y/ovFy
MdU3s8z7jyjZfG6robduOjPnV77Zb/hmRf/1dU+n+oq9dmG61XEzn83m5+fv
j5tu8ep48zWCx2Gs7yHU1yS7cqpZjr8U70T3HrrvOtK3hTbuKwH3vkDbfXCK
f/t8r/nVr2n9DdOK1NfdtnUfZLzzdYL+axvdWI+ny8aen5uZna+XgXWinSIW
2i6QptPNydCR/2bBm7570DW7P18skL3/OunpIO6X26+f7L+UbfwNaC9/w6JC
N5egmV++Hz7oun99f/QhNO5DlfQRX835uvVfFTz6euBgT/oHvboO9exRsJz7
DwnYbTR8WckkGL4UE23rExJQs15CWi6Hbwvtv/V2O50T911Gv4tPJv83csMT
/yW4AAA=

-->

</rfc>
