<?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.6.26 (Ruby 2.6.10) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-cfrg-schwabe-kyber-04" category="info" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.16.0 -->
  <front>
    <title abbrev="kyber">Kyber Post-Quantum KEM</title>
    <seriesInfo name="Internet-Draft" value="draft-cfrg-schwabe-kyber-04"/>
    <author fullname="Peter Schwabe">
      <organization>MPI-SP &amp; Radboud University</organization>
      <address>
      </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="January" day="02"/>
    <workgroup>None</workgroup>
    <keyword>kyber</keyword>
    <keyword>kem</keyword>
    <keyword>post-quantum</keyword>
    <abstract>
      <t>This memo specifies a preliminary version ("draft00", "v3.02")
    of Kyber, an IND-CCA2 secure Key Encapsulation Method.</t>
    </abstract>
    <note removeInRFC="true">
      <name>About This Document</name>
      <t>
        The latest revision of this draft can be found at <eref target="https://bwesterb.github.io/draft-schwabe-cfrg-kyber/draft-cfrg-schwabe-kyber.html"/>.
        Status information for this document may be found at <eref target="https://datatracker.ietf.org/doc/draft-cfrg-schwabe-kyber/"/>.
      </t>
      <t>Source for this draft and an issue tracker can be found at
        <eref target="https://github.com/bwesterb/draft-schwabe-cfrg-kyber"/>.</t>
    </note>
  </front>
  <middle>
    <section anchor="introduction">
      <name>Introduction</name>
      <t>Kyber is NIST's pick for a post-quantum key agreement <xref target="NISTR3"/>.</t>
      <t>Kyber is not a Diffie-Hellman (DH) style non-interactive key agreement,
but instead, Kyber is a Key Encapsulation Method (KEM).
A KEM is a three-tuple of algorithms (<em>KeyGen</em>, <em>Encapsulate</em>, <em>Decapsulate</em>):</t>
      <ul spacing="normal">
        <li>
          <em>KeyGen</em> takes no inputs and generates a private key and a public key;</li>
        <li>
          <em>Encapsulate</em> takes as input a public key and produces as output
a ciphertext and a shared secret;</li>
        <li>
          <em>Decapsulate</em> takes as input a ciphertext and a private key and
produces a shared secret.</li>
      </ul>
      <t>Like DH, a KEM can be used as an unauthenticated key-agreement
protocol, for example in TLS <xref target="HYBRID"/> <xref target="XYBERTLS"/>.
However, unlike DH, a KEM-based key agreement is <em>interactive</em>,
because the party executing Encapsulate can compute its protocol
message (the ciphertext) only after having received the input
(public key) from the party running <em>KeyGen</em> and <em>Decapsulate</em>.</t>
      <t>A KEM can be transformed into a PKE scheme using HPKE <xref target="RFC9180"/> <xref target="XYBERHPKE"/>.</t>
      <section anchor="warning-on-stability-and-relation-to-ml-kem">
        <name>Warning on stability and relation to ML-KEM</name>
        <t><strong>NOTE</strong> This draft is not stable and does not (yet) match the final
NIST standard ML-KEM (FIPS 203) expected in 2024. It also does not
match the draft for ML-KEM published by NIST August 2023. <xref target="MLKEM"/></t>
        <t>Currently it matches Kyber as submitted
to round 3 of the NIST PQC process <xref target="KYBERV302"/>.</t>
      </section>
    </section>
    <section anchor="conventions-and-definitions">
      <name>Conventions and Definitions</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>
    </section>
    <section anchor="overview">
      <name>Overview</name>
      <t>Kyber is an IND-CCA2 secure KEM. It is constructed by applying a
Fujisaki-Okamato style transformation on InnerPKE, which is
the underlying IND-CPA secure Public Key Encryption scheme.
We cannot use InnerPKE directly, as its ciphertexts are malleable.</t>
      <artwork><![CDATA[
                   F.O. transform
   InnerPKE   ---------------------->   Kyber
   IND-CPA                              IND-CCA2
]]></artwork>
      <t>Kyber is a lattice-based scheme. More precisely, its security
is based on the learning-with-errors-and-rounding problem in module
lattices (MLWER).
The underlying polynomial ring R (defined in <xref target="S-ring"/>) is chosen such that
multiplication is very fast using the number theoretic transform
(NTT, see <xref target="S-NTT"/>).</t>
      <t>An InnerPKE private key is a vector <em>s</em> over R of length k which is
<em>small</em> in a particular way. Here <tt>k</tt> is a security parameter akin to the
size of a prime modulus. For Kyber512, which targets AES-128's security level,
the value of k is 2, for Kyber768 (AES-192 security level) k is 3,
and for Kyber1024 (AES-256 security level) k is 4.</t>
      <t>The public key consists of two values:</t>
      <ul spacing="normal">
        <li>
          <em>A</em> a k-by-k matrix over R sampled uniformly at random <em>and</em></li>
        <li>
          <em>t = A s + e</em>, where <tt>e</tt> is a suitably small masking vector.</li>
      </ul>
      <t>Distinguishing between such A s + e and a uniformly sampled t is the
decision module learning-with-errors (MLWE) problem. If that is hard, then
it is also hard to recover the private key from the public key
as that would allow you to distinguish between those two.</t>
      <t>To save space in the public key, A is recomputed deterministically from
a 256bit seed <em>rho</em>. Strictly speaking, A is not uniformly random anymore,
but it's computationally indistuinguishable from it.</t>
      <t>A ciphertext for a message m under this public key is a pair (c_1, c_2)
computed roughly as follows:</t>
      <artwork><![CDATA[
c_1 = Compress(A^T r + e_1, d_u)
c_2 = Compress(t^T r + e_2 + Decompress(m, 1), d_v)
]]></artwork>
      <t>where</t>
      <ul spacing="normal">
        <li>e_1, e_2 and r are small blinds;</li>
        <li>Compress(-, d) removes some information, leaving d bits per coefficient
and Decompress is an "approximate inverse" of Compress;</li>
        <li>d_u, d_v are scheme parameters; and</li>
        <li>superscript T denotes transposition, so <em>A^T</em> is the transpose of A,
see <xref target="transpose"/> and <em>t^T r</em> is the dot product
of t and r, see <xref target="dot-prod"/>.</li>
      </ul>
      <t>Distinguishing such a ciphertext and uniformly sampled (c_1, c_2)
is an example of the full MLWER problem, see Section 4.4 of <xref target="KYBERV302"/>.</t>
      <t>To decrypt the ciphertext, one computes</t>
      <artwork><![CDATA[
m = Compress(Decompress(c_2, d_v) - s^T Decompress(c_1, d_u), 1).
]]></artwork>
      <t>It it not straight-forward to see that this formula is correct.
In fact, there is negligable but non-zero probability that a ciphertext
does not decrypt correctly given by the DFP column in <xref target="params"/>.
This failure probability can be computed by a careful automated
analysis of the probabilities involved, see <tt>kyber_failure.py</tt> of <xref target="SECEST"/>.</t>
      <t>To define all these operations precisely, we first define the field
of coefficients for our polynomial ring; what it means to be small;
and how to compress. Then we define the polynomial ring R; its operations
and in particular the NTT. We continue with the different methods of
sampling and (de)serialization. Then, we first define InnerPKE
and finally Kyber proper.</t>
    </section>
    <section anchor="the-field-gfq">
      <name>The field GF(q)</name>
      <t>Kyber is defined over GF(q) = Z/qZ, the integers modulo q = 13*2^8+1 = 3329.</t>
      <section anchor="size">
        <name>Size</name>
        <t>To define the size of a field element, we need a signed modulo. For any odd m,
we write</t>
        <artwork><![CDATA[
a smod m
]]></artwork>
        <t>for the unique integer b with -(m-1)/2 &lt; b &lt;= (m-1)/2 and b = a modulo m.</t>
        <t>To avoid confusion, for the more familiar modulo we write umod; that is</t>
        <artwork><![CDATA[
a umod m
]]></artwork>
        <t>is the unique integer b with 0 &lt;= b &lt; m and b = a modulo m.</t>
        <t>Now we can define the norm of a field element:</t>
        <artwork><![CDATA[
|| a || = abs(a smod q)
]]></artwork>
        <t>Examples:</t>
        <artwork><![CDATA[
 3325 smod q = -4        ||  3325 || = 4
-3320 smod q =  9        || -3320 || = 9
]]></artwork>
        <t><cref anchor="_4" source="Bas">TODO (#23) Should we define smod and umod at all, since we don't
use it.</cref></t>
      </section>
      <section anchor="compression">
        <name>Compression</name>
        <t>In several parts of the algorithm, we will need a method to compress
fied elements down into d bits. To do this, we use the following method.</t>
        <t>For any positive integer d, integer x and integer 0 &lt;= y &lt; 2^d, we define</t>
        <artwork><![CDATA[
  Compress(x, d) = Round( (2^d / q) x ) umod 2^d
Decompress(y, d) = Round( (q / 2^d) y )
]]></artwork>
        <t>where Round(x) rounds any fraction to the nearest integer going up with ties.</t>
        <t>Note that in <xref target="S-VectorOps"/> we extend Compress and Decompress
to polynomials and vectors of polynomials.</t>
        <t>These two operations have the following properties:</t>
        <ul spacing="normal">
          <li>
            <tt>0 &lt;= Compress(x, d) &lt; 2^d</tt></li>
          <li>
            <tt>0 &lt;= Decompress(y, d) &lt; q</tt></li>
          <li>
            <tt>Compress(Decompress(y, d), d) = y</tt></li>
          <li>If <tt>Decompress(Compress(x, d), d) = x'</tt>, then <tt>|| x' - x || &lt;= Round(q/2^(d+1))</tt></li>
          <li>If <tt>x = x' modulo q</tt>, then <tt>Compress(x, d) = Compress(x', d)</tt></li>
        </ul>
        <t>For implementation efficiency, these can be computed as follows.</t>
        <artwork><![CDATA[
  Compress(x, d) = Div( (x << d) + q/2), q ) & ((1 << d) - 1)
Decompress(y, d) = (q*y + (1 << (d-1))) >> d
]]></artwork>
        <t>where Div(x, q) = Floor(x / q). <cref anchor="_1" source="Bas">TODO Do we want to include the proof that this is correct?
Do we need to define &gt;&gt; and &lt;&lt;?</cref>
To prevent leaking the secret key,
    this must be computed in constant time <xref target="KYBERSLASH"/>.
On platforms where Div is not constant-time, the following
    equation is useful, which holds for those x
    that appear in the previous formula for 0 &lt; d &lt; 12.</t>
        <artwork><![CDATA[
Div(x, q) = (20642679 * x) >> 36
]]></artwork>
      </section>
    </section>
    <section anchor="S-ring">
      <name>The ring Rq</name>
      <t>Kyber is defined over a polynomial ring Rq = GF(q)[x]/(x^n+1)
where n=256 (and q=3329). Elements of Rq are tuples of 256 integers modulo q.
We will call them polynomials or elements interchangeably.</t>
      <t>A tuple a = (a_0, ..., a_255) represents the polynomial</t>
      <artwork><![CDATA[
a_0 + a_1 x + a_2 x^2 + ... + a_255 x^255.
]]></artwork>
      <t>With polynomial coefficients, vector and matrix indices, we will start
counting at zero.</t>
      <section anchor="operations">
        <name>Operations</name>
        <section anchor="size-of-polynomials">
          <name>Size of polynomials</name>
          <t>For a polynomial a = (a_0, ..., a_255) in R, we write:</t>
          <artwork><![CDATA[
|| a || = max_i || a_i ||
]]></artwork>
          <t>Thus a polynomial is considered large if one of its components is large.</t>
        </section>
        <section anchor="addition-and-subtraction">
          <name>Addition and subtraction</name>
          <t>Addition and subtraction of elements is componentwise. Thus</t>
          <artwork><![CDATA[
(a_0, ..., a_255) + (b_0, ..., b_255) = (a_0 + b_0, ..., a_255 + b_255),
]]></artwork>
          <t>and</t>
          <artwork><![CDATA[
(a_0, ..., a_255) - (b_0, ..., b_255) = (a_0 - b_0, ..., a_255 - b_255),
]]></artwork>
          <t>where addition/subtractoin in each component is computed modulo q.</t>
        </section>
        <section anchor="multiplication">
          <name>Multiplication</name>
          <t>Multiplication is that of polynomials (convolution) with the additional rule
that x^256=-1. To wit</t>
          <artwork><![CDATA[
(a_0, ..., a_255) \* (b_0, ..., b_255)
    = (a_0 * b_0 - a_255 * b_1 - ... - a_1 * b_255,
       a_0 * b_1 + a_1 * b_0 - a_255 * b_2 - ... - a_2 * b_255,

            ...

       a_0 * b_255 + ... + a_255 * b_0)
]]></artwork>
          <t>We will not use this schoolbook multiplication to compute the product.
Instead we will use the more efficient, number theoretic transform (NTT),
see <xref target="S-NTT"/>.</t>
          <section anchor="S-NTT">
            <name>Background on the Number Theoretic Transform (NTT)</name>
            <t>The modulus q was chosen such that 256 divides into q-1. This means that
there are zeta with</t>
            <artwork><![CDATA[
zeta^128 = -1  modulo  q
]]></artwork>
            <t>With such a zeta, we can almost completely split the polynomial x^256+1
used to define R over GF(q):</t>
            <artwork><![CDATA[
x^256 + 1 = x^256 - zeta^128
          = (x^128 - zeta^64)(x^128 + zeta^64)
          = (x^128 - zeta^64)(x^128 - zeta^192)
          = (x^64 - zeta^32)(x^64 + zeta^32)
                (x^64 - zeta^96)(x^64 + zeta^96)

            ...

          = (x^2 - zeta)(x^2 + zeta)(x^2 - zeta^65)(x^2 + zeta^65)
                    ... (x^2 - zeta^127)(x^2 + zeta^127)
]]></artwork>
            <t>Note that the powers of zeta that appear in the second, fourth, ...,
and final lines are in binary:</t>
            <artwork><![CDATA[
0100000 1100000
0010000 1010000 0110000 1110000
0001000 1001000 0101000 1101000 0011000 1011000 0111000 1111000
            ...
0000001 1000001 0100001 1100001 0010001 1010001 0110001 ... 1111111
]]></artwork>
            <t>That is: brv(2), brv(3), brv(4), ..., where brv(x) denotes the 7-bit
bitreversal of x. The final line is brv(64), brv(65), ..., brv(127).</t>
            <t>These polynomials x^2 +- zeta^i are irreducible and coprime, hence by
the Chinese Remainder Theorem for commutative rings, we know</t>
            <artwork><![CDATA[
R = GF(q)[x]/(x^256+1) -> GF(q)[x]/(x^2-zeta) x ... x GF(q)[x]/(x^2+zeta^127)
]]></artwork>
            <t>given by a |-&gt; ( a mod x^2 - zeta, ..., a mod x^2 + zeta^127 ) is an isomorphism.
This is the Number Theoretic Transform (NTT). Multiplication on the right is
much easier: it's almost componentwise, see <xref target="S-NTT-mul"/>.</t>
            <t>A propos, the the constant factors that appear in the moduli in order
can be computed efficiently as follows (all modulo q):</t>
            <artwork><![CDATA[
-zeta     = -zeta^brv(64)  = -zeta^{1 + 2 brv(0)}
 zeta     =  zeta^brv(64)  = -zeta^{1 + 2 brv(1)}
-zeta^65  = -zeta^brv(65)  = -zeta^{1 + 2 brv(2)}
 zeta^65  =  zeta^brv(65)  = -zeta^{1 + 2 brv(3)}
-zeta^33  = -zeta^brv(66)  = -zeta^{1 + 2 brv(4)}
 zeta^33  =  zeta^brv(66)  = -zeta^{1 + 2 brv(5)}

             ...

-zeta^127 = -zeta^brv(127) = -zeta^{1 + 2 brv(126)}
 zeta^127 =  zeta^brv(127) = -zeta^{1 + 2 brv(127)}
]]></artwork>
            <t>To compute a multiplication in R efficiently, one can first use the
NTT, to go to the right "into the NTT domain"; compute the multiplication
there and move back with the inverse NTT.</t>
            <t>The NTT can be computed efficiently by performing each binary split
of the polynomial separately as follows:</t>
            <artwork><![CDATA[
a |-> ( a mod x^128 - zeta^64, a mod x^128 + zeta^64 ),
  |-> ( a mod  x^64 - zeta^32, a mod  x^64 + zeta^32,
        a mod  x^64 - zeta^96, a mod  x^64 + zeta^96 ),

    et cetera
]]></artwork>
            <t>If we concatenate the resulting coefficients, expanding the definitions,
for the first step we get:</t>
            <artwork><![CDATA[
a |-> (   a_0 + zeta^64 a_128,   a_1 + zeta^64 a_129,
         ...
        a_126 + zeta^64 a_254, a_127 + zeta^64 a_255,
          a_0 - zeta^64 a_128,   a_1 - zeta^64 a_129,
         ...
        a_126 - zeta^64 a_254, a_127 - zeta^64 a_255)
]]></artwork>
            <t>We can see this as 128 applications of the linear map CT_64, where</t>
            <artwork><![CDATA[
CT_i: (a, b) |-> (a + zeta^i b, a - zeta^i b)   modulo q
]]></artwork>
            <t>for the appropriate i in the following order, pictured in the case of n=16:</t>
            <artwork><![CDATA[
-x----------------x--------x---
-|-x--------------|-x------|-x-
-|-|-x------------|-|-x----x-|-
-|-|-|-x----------|-|-|-x----x-
-|-|-|-|-x--------x-|-|-|--x---
-|-|-|-|-|-x--------x-|-|--|-x-
-|-|-|-|-|-|-x--------x-|--x-|-
-|-|-|-|-|-|-|-x--------x----x-
-x-|-|-|-|-|-|-|--x--------x---
---x-|-|-|-|-|-|--|-x------|-x-
-----x-|-|-|-|-|--|-|-x----x-|-
-------x-|-|-|-|--|-|-|-x----x-
---------x-|-|-|--x-|-|-|--x---
-----------x-|-|----x-|-|--|-x-
-------------x-|------x-|--x-|-
---------------x--------x----x-
]]></artwork>
            <t>For n=16 there are 3 levels with 1, 2 and 4 row groups respectively.
For the full n=256, there are 7 levels with 1, 2, 4, 8, 16, 32 and 64
row groups respectively. The appropriate power of zeta in the first
level is brv(1)=64. The second level has brv(2) and brv(3) as powers
of zeta for the top and bottom row group respectively, and so on.</t>
            <t>The CT_i is known as a Cooley-Tukey butterfly. Its inverse is given
by the Gentleman-Sande butterfly:</t>
            <artwork><![CDATA[
GS_i: (a, b) |-> ( (a+b)/2, zeta^-i (a-b)/2 )    modulo q
]]></artwork>
            <t>The inverse NTT can be computed by replacing CS_i by GS_i and flipping
the diagram horizontally. <cref anchor="_2" source="Bas">TODO (#8) This section gives background not necessary for the implementation.
Should we keep it?</cref></t>
            <section anchor="optimization-notes">
              <name>Optimization notes</name>
              <t>The modular divisions by two in the InvNTT can be collected into a
single modular division by 128.</t>
              <t>zeta^-i can be computed as -zeta^(128-i), which allows one to use the same
precomputed table of powers of zeta for both the NTT and InvNTT.</t>
              <t><cref anchor="_3" source="Bas">TODO Add hints on lazy Montgomery reduction? Including
https://eprint.iacr.org/2020/1377.pdf</cref></t>
            </section>
          </section>
          <section anchor="ntt-and-invntt">
            <name>NTT and InvNTT</name>
            <t>As primitive 256th root of unity we use zeta=17.</t>
            <t>As before, brv(i) denotes the 7-bit bitreversal of i, so brv(1)=64
and brv(91)=109.</t>
            <t>The NTT is a linear bijection R -&gt; R given by the matrix:</t>
            <artwork><![CDATA[
             [ zeta^{ (2 brv(i>>1) + 1) (j>>1) }               if i=j mod 2
(NTT)_{ij} = [
             [ 0                                               otherwise
]]></artwork>
            <t>Recall that we start counting rows and columns at zero.  The NTT can be
computed more efficiently as described in section <xref target="S-NTT"/>.</t>
            <t>The inverse of the NTT is called InvNTT. It is given by the matrix:</t>
            <artwork><![CDATA[
                    [ zeta^{ 256 - (2 brv(j>>1) + 1) (i>>1) }  if i=j mod 2
128 (InvNTT)_{ij} = [
                    [ 0                                        otherwise
]]></artwork>
            <t>Examples:</t>
            <artwork><![CDATA[
NTT(1, 1, 0, ..., 0)   = (1, 1, ..., 1, 1)
NTT(0, 1, 2, ..., 255) = (2429, 2845, 425, 1865, ..., 2502, 2134, 2717, 2303)
]]></artwork>
          </section>
          <section anchor="S-NTT-mul">
            <name>Multiplication in NTT domain</name>
            <t>For elements a, b in R, we write a o b for multiplication in the NTT domain.
That is: a * b = InvNTT(NTT(a) o NTT(b)). Concretely:</t>
            <artwork><![CDATA[
            [ a_i b_i + zeta^{2 brv(i >> 1) + 1} a_{i+1} b_{i+1}   if i even
(a o b)_i = [
            [ a_{i-1} b_i + a_i b_{i-1}                            otherwise
]]></artwork>
          </section>
        </section>
      </section>
    </section>
    <section anchor="symmetric-cryptographic-primitives">
      <name>Symmetric cryptographic primitives</name>
      <t>Kyber makes use of various symmertic primitives PRF, XOF, KDF, H and G, where</t>
      <artwork><![CDATA[
XOF(seed) = SHAKE-128(seed)
PRF(seed, counter) = SHAKE-256(seed || counter)
KDF(prekey) = SHAKE-256(msg)[:32]
H(msg) = SHA3-256(msg)
G(msg) = (SHA3-512(msg)[:32], SHA3-512(msg)[32:])
]]></artwork>
      <t>Here <tt>counter</tt> is an octet; <tt>seed</tt> is 32 octets; <tt>prekey</tt> is 64 octets;
and the length of <tt>msg</tt> varies.</t>
      <t>On the surface, they look different, but they are all based on
the same flexible Keccak XOF that uses the f1600 permutation,
see <xref target="FIPS202"/>:</t>
      <artwork><![CDATA[
XOF(seed)      =  Keccak[256](seed || 1111, .)
PRF(seed, ctr) =  Keccak[512](seed || ctr || 1111, .)
KDF(prekey)    =  Keccak[512](prekey || 1111, 256)
H(msg)         =  Keccak[512](msg || 01, 256)
G(msg)         = (Keccak[1024](msg || 01, 512)[:32],
                  Keccak[1024](msg || 01, 512)[32:])

Keccak[c] = Sponge[Keccak-f[1600], pad10*1, 1600-c]
]]></artwork>
      <t>The reason five different primitives are used is to ensure domain
separation, which is crucial for security, cf. <xref target="H2CURVE"/> §2.2.5.
Additionally, a smaller sponge capacity is used for performance
where permissable by the security requirements.</t>
    </section>
    <section anchor="sampling-of-polynomials">
      <name>Sampling of polynomials</name>
      <section anchor="uniformly">
        <name>Uniformly</name>
        <t>The polynomials in the matrix A are sampled uniformly and deterministically
from an octet stream (XOF) using rejection sampling as follows.</t>
        <t>Three octets b_0, b_1, b_2 are read from the stream at a time. These are
interpreted as two 12-bit unsigned integers d_1, d_2 via</t>
        <artwork><![CDATA[
d_1 + d_2 2^12 = b_0 + b_1 2^8 + b_2 2^16
]]></artwork>
        <t>This creates a stream of 12-bit <tt>d</tt>s. Of these, the elements &gt;= q are
ignored. From the resultant stream, the coefficients of the polynomial
are taken in order. In Python:</t>
        <artwork><![CDATA[
def sampleUniform(stream):
    cs = []
    while True:
        b = stream.read(3)
        d1 = b[0] + 256*(b[1] % 16)
        d2 = (b[1] >> 4) + 16*b[2]
        for d in [d1, d2]:
            if d >= q: continue
            cs.append(d)
            if len(cs) == n: return Poly(cs)
]]></artwork>
        <t>Example:</t>
        <artwork><![CDATA[
sampleUniform(SHAKE-128('')) = (3199, 697, 2212, 2302, ..., 255, 846, 1)
]]></artwork>
        <section anchor="samplematrix">
          <name>sampleMatrix</name>
          <t>Now, the <em>k</em> by <em>k</em> matrix <em>A</em> over <em>R</em> is derived deterministically
from a 32-octet seed <em>rho</em> using sampleUniform as follows.</t>
          <artwork><![CDATA[
sampleMatrix(rho)_{ij} = sampleUniform(XOF(rho || octet(j) || octet(i))
]]></artwork>
          <t>That is, to derive the polynomial at the <em>i</em>th row and <em>j</em>th column,
sampleUniform is called with the 34-octet seed created by first appending
the octet <em>j</em> and then the octet <em>i</em> to <em>rho</em>. Recall that we start counting
rows and columns from zero.</t>
          <t>As the NTT is a bijection, it does not matter whether we interpret
the polynomials of the sampled matrix in the NTT domain or not.
For efficiency, we do interpret the sampled matrix in the NTT domain.</t>
        </section>
      </section>
      <section anchor="from-a-binomial-distribution">
        <name>From a binomial distribution</name>
        <t>Noise is sampled from a centered binomial distribution Binomial(2eta, 1/2) - eta
deterministically  as follows.</t>
        <t>An octet array a of length 64*eta is converted to a polynomial CBD(a, eta)</t>
        <artwork><![CDATA[
CBD(a, eta)_i = b_{2i eta} + b_{2i eta + 1} + ... + b_{2i eta + eta-1}
              - b_{2i eta + eta} - ... - b_{2i eta + 2eta - 1},
]]></artwork>
        <t>where b = OctetsToBits(a).</t>
        <t>Examples:</t>
        <artwork><![CDATA[
CBD((0, 1, 2, ..., 127), 2) = (0, 0, 1, 0, 1, 0, ..., 3328, 1, 0, 1)
CBD((0, 1, 2, ..., 191), 3) = (0, 1, 3328, 0, 2, ..., 3328, 3327, 3328, 1)
]]></artwork>
        <section anchor="samplenoise">
          <name>sampleNoise</name>
          <t>A <em>k</em> component small vector <em>v</em> is derived from a seed 32-octet seed <em>sigma</em>,
an offset <em>offset</em> and size <em>eta</em> as follows:</t>
          <artwork><![CDATA[
sampleNoise(sigma, eta, offset)_i = CBD(PRF(sigma, octet(i+offset)), eta)
]]></artwork>
          <t>Recall that we start counting vector indices at zero.</t>
        </section>
      </section>
    </section>
    <section anchor="vector-and-matrices">
      <name>Vector and matrices</name>
      <section anchor="S-VectorOps">
        <name>Operations on vectors</name>
        <t>Recall that Compress(x, d) maps a field element x into {0, ..., 2^d-1}.
In Kyber d is at most 11 and so we can interpret Compress(x, d) as a field
element again.</t>
        <t>In this way, we can extend Compress(-, d) to polynomials by applying
to each coefficient separately and in turn to vectors by applying
to each polynomial. That is, for a vector v and polynomial p:</t>
        <artwork><![CDATA[
Compress(p, d)_i = Compress(p_i, d)
Compress(v, d)_i = Compress(v_i, d)
]]></artwork>
      </section>
      <section anchor="dot-prod">
        <name>Dot product and matrix multiplication</name>
        <t>We will also use "o", from section <xref target="S-NTT-mul"/>,
to denote the dot product and matrix multiplication
in the NTT domain. Concretely:</t>
        <ol spacing="normal" type="1"><li>
            <t>For two length k vector v and w, we write  </t>
            <artwork><![CDATA[
 v o w = v_0 o w_0 + ... + v_{k-1} o w_{k-1}
]]></artwork>
          </li>
          <li>
            <t>For a k by k matrix A and a length k vector v, we have  </t>
            <artwork><![CDATA[
 (A o v)_i = A_i o v,
]]></artwork>
            <t>
where A_i denotes the (i+1)th row of the matrix A as we start
counting at zero.</t>
          </li>
        </ol>
      </section>
      <section anchor="transpose">
        <name>Transpose</name>
        <t>For a matrix A, we denote by A^T the tranposed matrix. To wit:</t>
        <artwork><![CDATA[
A^T_ij = A_ji.
]]></artwork>
        <t>We define Decompress(-, d) for vectors and polynomials in the same way.</t>
      </section>
    </section>
    <section anchor="serialization">
      <name>Serialization</name>
      <section anchor="octetstobits">
        <name>OctetsToBits</name>
        <t>For any list of octets a_0, ..., a_{s-1}, we define OctetsToBits(a), which
is a list of bits of length 8s, defined by</t>
        <artwork><![CDATA[
OctetsToBits(a)_i = ((a_(i>>3)) >> (i umod 8)) umod 2.
]]></artwork>
        <t>Example:</t>
        <artwork><![CDATA[
OctetsToBits(12,45) = (0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0)
]]></artwork>
      </section>
      <section anchor="encode-and-decode">
        <name>Encode and Decode</name>
        <t>For an integer 0 &lt; w &lt;= 12, we define Decode(a, w), which converts
any  list a of w*l/8 octets into a list of length l with
values in {0, ..., 2^w-1} as follows.</t>
        <artwork><![CDATA[
Decode(a, w)_i = b_{wi} + b_{wi+1} 2 + b_{wi+2} 2^2 + ... + b_{wi+w-1} 2^{w-1},
]]></artwork>
        <t>where b = OctetsToBits(a).</t>
        <t>Encode(-, w) is the unique inverse of Decode(-, w)</t>
        <section anchor="polynomials">
          <name>Polynomials</name>
          <t>A polynomial p is encoded by passing its coefficients to Encode:</t>
          <artwork><![CDATA[
EncodePoly(p, w) = Encode(p_0, p_1, ..., p_{n-1}, w)
]]></artwork>
          <t>DecodePoly(-, w) is the unique inverse of EncodePoly(-, w).</t>
        </section>
        <section anchor="vectors">
          <name>Vectors</name>
          <t>A vector v of length k over R is encoded by concatenating the coefficients
in the obvious way:</t>
          <artwork><![CDATA[
EncodeVec(v, w) = Encode((v_0)_0, ..., (v_0)_{n-1},
                         (v_1)_{0}, ..., (v_1)_{n-1},
                                ..., (v_{k-1})_{n-1}, w)
]]></artwork>
          <t>DecodeVec(-, w) is the unique inverse of EncodeVec(-, w).</t>
        </section>
      </section>
    </section>
    <section anchor="inner-malleable-public-key-encryption-scheme">
      <name>Inner malleable public-key encryption scheme</name>
      <t>We are ready to define the IND-CPA secure Public-Key Encryption scheme that
underlies Kyber. It is unsafe to use this underlying scheme directly as
its ciphertexts are malleable. Instead, a Public-Key Encryption scheme
can be constructed on top of Kyber by using HPKE <xref target="RFC9180"/> <xref target="XYBERHPKE"/>.</t>
      <section anchor="parameters">
        <name>Parameters</name>
        <t>We have already been introduced to the following parameters:</t>
        <dl>
          <dt><em>q</em></dt>
          <dd>
            <t>Order of field underlying <em>R</em>.</t>
          </dd>
          <dt><em>n</em></dt>
          <dd>
            <t>Length of polynomials in <em>R</em>.</t>
          </dd>
          <dt><em>zeta</em></dt>
          <dd>
            <t>Primitive root of unity in GF(q), used for NTT in R.</t>
          </dd>
          <dt><em>XOF</em>, <em>H</em>, <em>G</em>, <em>PRF</em>, <em>KDF</em></dt>
          <dd>
            <t>Various symmetric primitives.</t>
          </dd>
          <dt><em>k</em></dt>
          <dd>
            <t>Main security parameter: the number of rows and columns in the matrix <em>A</em>.</t>
          </dd>
        </dl>
        <t>Additionally, Kyber takes the following parameters</t>
        <dl>
          <dt><em>eta1</em>, <em>eta2</em></dt>
          <dd>
            <t>Size of small coefficients used in the private key and noise vectors.</t>
          </dd>
          <dt><em>d_u</em>, <em>d_v</em></dt>
          <dd>
            <t>How many bits to retain per coefficient of the <em>u</em> and <em>v</em> components
of the ciphertext.</t>
          </dd>
        </dl>
        <t>The values of these parameters are given in <xref target="S-params"/>.</t>
      </section>
      <section anchor="key-generation">
        <name>Key generation</name>
        <t>InnerKeyGen(seed) takes a 32 octet <strong>seed</strong> and deterministically
produces a keypair as follows.</t>
        <ol spacing="normal" type="1"><li>Set (rho, sigma) = G(seed).</li>
          <li>
            <t>Derive
            </t>
            <ol spacing="normal" type="1"><li>AHat = sampleMatrix(rho).</li>
              <li>s = sampleNoise(sigma, eta1, 0)</li>
              <li>e = sampleNoise(sigma, eta1, k)</li>
            </ol>
          </li>
          <li>
            <t>Compute
            </t>
            <ol spacing="normal" type="1"><li>sHat = NTT(s)</li>
              <li>tHat = AHat o sHat + NTT(e)</li>
            </ol>
          </li>
          <li>
            <t>Return
            </t>
            <ol spacing="normal" type="1"><li>publicKey = EncodeVec(tHat, 12) || rho</li>
              <li>privateKey = EncodeVec(sHat, 12)</li>
            </ol>
          </li>
        </ol>
        <t>Note that in essence we're simply computing t = A s + e.</t>
      </section>
      <section anchor="encryption">
        <name>Encryption</name>
        <t>InnerEnc(msg, publicKey, seed) takes a 32-octet seed,
and deterministically encrypts the 32-octet msg for the InnerPKE public
key publicKey as follows.</t>
        <ol spacing="normal" type="1"><li>
            <t>Split publicKey into
            </t>
            <ol spacing="normal" type="1"><li>k*(n/8)*12-octet tHatPacked</li>
              <li>32-octet rho</li>
            </ol>
          </li>
          <li>Parse tHat = DecodeVec(tHatPacked, 12)</li>
          <li>
            <t>Derive
            </t>
            <ol spacing="normal" type="1"><li>AHat = sampleMatrix(rho)</li>
              <li>r = sampleNoise(seed, eta1, 0)</li>
              <li>e_1 = sampleNoise(seed, eta2, k)</li>
              <li>e_2 = sampleNoise(seed, eta2, 2k)_0</li>
            </ol>
          </li>
          <li>
            <t>Compute
            </t>
            <ol spacing="normal" type="1"><li>rHat = NTT(r)</li>
              <li>u = InvNTT(AHat^T o rHat) + e_1</li>
              <li>v = InvNTT(tHat o rHat) + e_2 + Decompress(DecodePoly(msg, 1), 1)</li>
              <li>c_1 = EncodeVec(Compress(u, d_u), d_u)</li>
              <li>c_2 = EncodePoly(Compress(v, d_v), d_v)</li>
            </ol>
          </li>
          <li>
            <t>Return
            </t>
            <ol spacing="normal" type="1"><li>cipherText = c_1 || c_2</li>
            </ol>
          </li>
        </ol>
      </section>
      <section anchor="decryption">
        <name>Decryption</name>
        <t>InnerDec(cipherText, privateKey) takes an InnerPKE private key
privateKey and decrypts a cipher text cipherText as follows.</t>
        <ol spacing="normal" type="1"><li>
            <t>Split cipherText into
            </t>
            <ol spacing="normal" type="1"><li>d_u*k*n/8-octet c_1</li>
              <li>d_v*n/8-octet c_2</li>
            </ol>
          </li>
          <li>
            <t>Parse
            </t>
            <ol spacing="normal" type="1"><li>u = Decompress(DecodeVec(c_1, d_u), d_u)</li>
              <li>v = Decompress(DecodePoly(c_2, d_v), d_v)</li>
              <li>sHat = DecodeVec(privateKey, 12)</li>
            </ol>
          </li>
          <li>
            <t>Compute
            </t>
            <ol spacing="normal" type="1"><li>m = v - InvNTT(sHat o NTT(u))</li>
            </ol>
          </li>
          <li>
            <t>Return
            </t>
            <ol spacing="normal" type="1"><li>plainText = EncodePoly(Compress(m, 1), 1)</li>
            </ol>
          </li>
        </ol>
      </section>
    </section>
    <section anchor="kyber">
      <name>Kyber</name>
      <t>Now we are ready to define Kyber itself.</t>
      <section anchor="key-generation-1">
        <name>Key generation</name>
        <t>A Kyber keypair is derived deterministically from a 64-octet seed as follows.</t>
        <ol spacing="normal" type="1"><li>
            <t>Split seed into
            </t>
            <ol spacing="normal" type="1"><li>A 32-octet cpaSeed</li>
              <li>A 32-octet z</li>
            </ol>
          </li>
          <li>
            <t>Compute
            </t>
            <ol spacing="normal" type="1"><li>(cpaPublicKey, cpaPrivateKey) = InnerKeyGen(cpaSeed)</li>
              <li>h = H(cpaPublicKey)</li>
            </ol>
          </li>
          <li>
            <t>Return
            </t>
            <ol spacing="normal" type="1"><li>publicKey = cpaPublicKey</li>
              <li>privateKey = cpaPrivateKey || cpaPublicKey || h || z</li>
            </ol>
          </li>
        </ol>
      </section>
      <section anchor="encapsulation">
        <name>Encapsulation</name>
        <t>Kyber encapsulation takes a public key and generates a shared secret
and ciphertext for the public key as follows.</t>
        <ol spacing="normal" type="1"><li>Sample secret cryptographically-secure random 32-octet seed.</li>
          <li>
            <t>Compute
            </t>
            <ol spacing="normal" type="1"><li>m = H(seed)</li>
              <li>(Kbar, cpaSeed) = G(m || H(publicKey))</li>
              <li>cpaCipherText = InnerEnc(m, publicKey, cpaSeed)</li>
            </ol>
          </li>
          <li>
            <t>Return
            </t>
            <ol spacing="normal" type="1"><li>cipherText = cpaCipherText</li>
              <li>sharedSecret = KDF(KBar || H(cpaCipherText))</li>
            </ol>
          </li>
        </ol>
      </section>
      <section anchor="S-decaps">
        <name>Decapsulation</name>
        <t>Kyber decapsulation takes a private key and a cipher text and
returns a shared secret as follows.</t>
        <ol spacing="normal" type="1"><li>
            <t>Split privateKey into
            </t>
            <ol spacing="normal" type="1"><li>A 12*k*n/8-octet cpaPrivateKey</li>
              <li>A 12*k*n/8+32-octet cpaPublicKey</li>
              <li>A 32-octet h</li>
              <li>A 32-octet z</li>
            </ol>
          </li>
          <li>
            <t>Compute
            </t>
            <ol spacing="normal" type="1"><li>m2 = InnerDec(cipherText, cpaPrivateKey)</li>
              <li>(KBar2, cpaSeed2) = G(m2 || h)</li>
              <li>cipherText2 = InnerEnc(m2, cpaPublicKey, cpaSeed2)</li>
              <li>K1 = KDF(KBar2 || H(cipherText))</li>
              <li>K2 = KDF(z || H(cipherText))</li>
            </ol>
          </li>
          <li>In constant-time, set K = K1 if cipherText == cipherText2 else set K = K2.</li>
          <li>
            <t>Return
            </t>
            <ol spacing="normal" type="1"><li>sharedSecret = K</li>
            </ol>
          </li>
        </ol>
        <t>For security, the implementation <bcp14>MUST NOT</bcp14> explicitly return
or otherwise leak via a side-channel, decapsulation succeeded,
viz <tt>cipherText == cipherText2</tt>.</t>
      </section>
    </section>
    <section anchor="S-params">
      <name>Parameter sets</name>
      <table anchor="params-comm">
        <name>Common parameters to all versions of Kyber</name>
        <thead>
          <tr>
            <th align="right">Name</th>
            <th align="center">Value</th>
            <th align="left">Description</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="right">q</td>
            <td align="center">3329</td>
            <td align="left">Order of base field</td>
          </tr>
          <tr>
            <td align="right">n</td>
            <td align="center">256</td>
            <td align="left">Degree of polynomials</td>
          </tr>
          <tr>
            <td align="right">zeta</td>
            <td align="center">17</td>
            <td align="left">nth root of unity in base field</td>
          </tr>
        </tbody>
      </table>
      <table anchor="params-symm">
        <name>Instantiation of symmetric primitives in Kyber</name>
        <thead>
          <tr>
            <th align="right">Primitive</th>
            <th align="left">Instantiation</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="right">XOF</td>
            <td align="left">SHAKE-128</td>
          </tr>
          <tr>
            <td align="right">H</td>
            <td align="left">SHA3-256</td>
          </tr>
          <tr>
            <td align="right">G</td>
            <td align="left">SHA3-512</td>
          </tr>
          <tr>
            <td align="right">PRF(s,b)</td>
            <td align="left">SHAKE-256(s || b)</td>
          </tr>
          <tr>
            <td align="right">KDF</td>
            <td align="left">SHAKE-256</td>
          </tr>
        </tbody>
      </table>
      <table anchor="params-desc">
        <name>Description of kyber parameters</name>
        <thead>
          <tr>
            <th align="right">Name</th>
            <th align="left">Description</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="right">k</td>
            <td align="left">Dimension of module</td>
          </tr>
          <tr>
            <td align="right">eta1, eta2</td>
            <td align="left">Size of "small" coefficients used in the private key and noise vectors.</td>
          </tr>
          <tr>
            <td align="right">d_u</td>
            <td align="left">How many bits to retain per coefficient of <tt>u</tt>, the private-key independent part of the ciphertext</td>
          </tr>
          <tr>
            <td align="right">d_v</td>
            <td align="left">How many bits to retain per coefficient of <tt>v</tt>, the private-key dependent part of the ciphertext.</td>
          </tr>
        </tbody>
      </table>
      <table anchor="params">
        <name>Kyber parameter sets with NIST security level (sec) and decryption failure probability (DFP)</name>
        <thead>
          <tr>
            <th align="right">Parameter set</th>
            <th align="center">k</th>
            <th align="center">eta1</th>
            <th align="center">eta2</th>
            <th align="center">d_u</th>
            <th align="center">d_v</th>
            <th align="center">sec</th>
            <th align="center">DFP</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="right">Kyber512</td>
            <td align="center">2</td>
            <td align="center">3</td>
            <td align="center">2</td>
            <td align="center">10</td>
            <td align="center">4</td>
            <td align="center">I</td>
            <td align="center">2^-139</td>
          </tr>
          <tr>
            <td align="right">Kyber768</td>
            <td align="center">3</td>
            <td align="center">2</td>
            <td align="center">2</td>
            <td align="center">10</td>
            <td align="center">4</td>
            <td align="center">III</td>
            <td align="center">2^-164</td>
          </tr>
          <tr>
            <td align="right">Kyber1024</td>
            <td align="center">4</td>
            <td align="center">2</td>
            <td align="center">2</td>
            <td align="center">11</td>
            <td align="center">5</td>
            <td align="center">V</td>
            <td align="center">2^-174</td>
          </tr>
        </tbody>
      </table>
      <table anchor="sizes">
        <name>Kyber parameter sets with sizes of shared secret (ss), public key (pk), cipher text (ct) and private key (sk)</name>
        <thead>
          <tr>
            <th align="right">Parameter set</th>
            <th align="center">ss</th>
            <th align="center">pk</th>
            <th align="center">ct</th>
            <th align="center">sk</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="right">Kyber512</td>
            <td align="center">32</td>
            <td align="center">800</td>
            <td align="center">768</td>
            <td align="center">1632</td>
          </tr>
          <tr>
            <td align="right">Kyber768</td>
            <td align="center">32</td>
            <td align="center">1184</td>
            <td align="center">1088</td>
            <td align="center">2400</td>
          </tr>
          <tr>
            <td align="right">Kyber1024</td>
            <td align="center">32</td>
            <td align="center">1568</td>
            <td align="center">1568</td>
            <td align="center">3168</td>
          </tr>
        </tbody>
      </table>
    </section>
    <section anchor="S-spec">
      <name>Machine-readable specification</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 PRF(seed, nonce):
    assert len(seed) == 32
    h = SHAKE256.new()
    h.update(seed + bytes([nonce]))
    return h

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

def H(msg): return hashlib.sha3_256(msg).digest()
def KDF(msg): return hashlib.shake_256(msg).digest(length=32)

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(PRF(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)
    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)
    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

    m = H(seed)
    Kbar, r = G(m + H(pk))
    ct = InnerEnc(pk, m, r, params)
    K = KDF(Kbar + H(ct))
    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)
    Kbar2, r2 = G(m2 + h)
    ct2 = InnerEnc(pk, m2, r2, params)
    return constantTimeSelectOnEquality(
        ct2, ct,
        KDF(Kbar2 + H(ct)),  # if ct == ct2
        KDF(z + H(ct)),      # if ct != ct2
    )
]]></artwork>
    </section>
    <section anchor="security-considerations">
      <name>Security Considerations</name>
      <t>Kyber512, Kyber768 and Kyber1024 are designed to be post-quantum
IND-CCA2 secure KEMs, at the security levels of AES-128, AES-192 and AES-256.</t>
      <t>The designers of Kyber recommend Kyber768.</t>
      <t>The inner public key encryption <bcp14>SHOULD NOT</bcp14> be used directly,
as its ciphertexts are malleable.  Instead, for public key encryption,
HPKE can be used to turn Kyber into IND-CCA2 secure PKE <xref target="RFC9180"/> <xref target="XYBERHPKE"/>.</t>
      <t>Any implementation <bcp14>MUST</bcp14> use implicit rejection as specified in <xref target="S-decaps"/>.</t>
    </section>
  </middle>
  <back>
    <references>
      <name>References</name>
      <references>
        <name>Normative References</name>
        <reference anchor="FIPS202" target="https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf">
          <front>
            <title>FIPS PUB 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="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>
        <name>Informative References</name>
        <reference anchor="KYBERV302" target="https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf">
          <front>
            <title>CRYSTALS-Kyber, Algorithm Specification And Supporting Documentation (version 3.02)</title>
            <author initials="R." surname="Avanzi">
              <organization/>
            </author>
            <author initials="J." surname="Bos">
              <organization/>
            </author>
            <author initials="L." surname="Ducas">
              <organization/>
            </author>
            <author initials="E." surname="Kiltz">
              <organization/>
            </author>
            <author initials="T." surname="Lepoint">
              <organization/>
            </author>
            <author initials="V." surname="Lyubashevsky">
              <organization/>
            </author>
            <author initials="J." surname="Schanck">
              <organization/>
            </author>
            <author initials="P." surname="Schwabe">
              <organization/>
            </author>
            <author initials="G." surname="Seiler">
              <organization/>
            </author>
            <author initials="D." surname="Stehle">
              <organization/>
            </author>
            <date year="2021"/>
          </front>
        </reference>
        <reference anchor="MLKEM" target="https://csrc.nist.gov/pubs/fips/203/ipd">
          <front>
            <title>FIPS 203 (Initial Draft): 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="SECEST" target="https://github.com/pq-crystals/security-estimates">
          <front>
            <title>CRYSTALS security estimate scripts</title>
            <author initials="L." surname="Ducas">
              <organization/>
            </author>
            <author initials="J." surname="Schanck">
              <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="NISTR3" target="https://csrc.nist.gov/News/2022/pqc-candidates-to-be-standardized-and-round-4">
          <front>
            <title>PQC Standardization Process: Announcing Four Candidates to be Standardized, Plus Fourth Round Candidates</title>
            <author initials="" surname="The NIST PQC Team">
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </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="H2CURVE">
          <front>
            <title>Hashing to Elliptic Curves</title>
            <author fullname="Armando Faz-Hernandez" initials="A." surname="Faz-Hernandez">
              <organization>Cloudflare, Inc.</organization>
            </author>
            <author fullname="Sam Scott" initials="S." surname="Scott">
              <organization>Cornell Tech</organization>
            </author>
            <author fullname="Nick Sullivan" initials="N." surname="Sullivan">
              <organization>Cloudflare, Inc.</organization>
            </author>
            <author fullname="Riad S. Wahby" initials="R. S." surname="Wahby">
              <organization>Stanford University</organization>
            </author>
            <author fullname="Christopher A. Wood" initials="C. A." surname="Wood">
              <organization>Cloudflare, Inc.</organization>
            </author>
            <date day="15" month="June" year="2022"/>
            <abstract>
              <t>This document specifies a number of algorithms for encoding or hashing an arbitrary string to a point on an elliptic curve.  This document is a product of the Crypto Forum Research Group (CFRG) in the IRTF.
              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-irtf-cfrg-hash-to-curve-16"/>
        </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="4" month="May" year="2023"/>
            <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-02"/>
        </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="KYBERSLASH" target="https://kyberslash.cr.yp.to">
          <front>
            <title>KyberSlash: division timings depending on secrets in Kyber software</title>
            <author initials="D. J." surname="Bernstein">
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
      </references>
    </references>
    <section anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>The authors would like to thank
C. Wood,
Florence D.,
I. Liusvaara,
J. Crawford,
J. Schanck,
M. Thomson, and
N. Sullivan
for their input and assistance.</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-schwabe-cfrg-kyber-03">
        <name>Since draft-schwabe-cfrg-kyber-03</name>
        <ul spacing="normal">
          <li>Add note on KyberSlash.</li>
        </ul>
      </section>
      <section anchor="since-draft-schwabe-cfrg-kyber-02">
        <name>Since draft-schwabe-cfrg-kyber-02</name>
        <ul spacing="normal">
          <li>Fix a typo in the machine-readable specification, and use a proper
SHAKE implementation. #5</li>
          <li>Add table with sizes.</li>
          <li>Reordered sections.</li>
          <li>Add reference to Kyber in HPKE.</li>
          <li>Miscellaneous editorial changes.</li>
          <li>Remove encapsulation seed as an explicit parameter in the written
specification.</li>
          <li>Write security recommendations. #18</li>
          <li>Explain relation with ML-KEM.</li>
        </ul>
      </section>
      <section anchor="since-draft-schwabe-cfrg-kyber-01">
        <name>Since draft-schwabe-cfrg-kyber-01</name>
        <ul spacing="normal">
          <li>Fix various typos.</li>
          <li>Move sections around.</li>
          <li>Elaborate domain separation and encoding of nonces
in symmetric primitives.</li>
          <li>Add explicit formula for InvNTT.</li>
          <li>Add acknowledgements.</li>
        </ul>
      </section>
      <section anchor="since-draft-schwabe-cfrg-kyber-00">
        <name>Since draft-schwabe-cfrg-kyber-00</name>
        <ul spacing="normal">
          <li>Test specification against NIST test vectors.</li>
          <li>
            <t>Fix two unintentional mismatches between this document
and the reference implementation:  </t>
            <ol spacing="normal" type="1"><li>KDF uses SHAKE-256 instead of SHAKE-128.</li>
              <li>Reverse order of seed. (<tt>z</tt> comes at the end.)</li>
            </ol>
          </li>
          <li>Elaborate text in particular introduction, and symmetric key section.</li>
        </ul>
      </section>
    </section>
  </back>
  <!-- ##markdown-source:
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
aMAvpjDncRSsVmAHsl/jveP9P26i7XV9ogAA

-->

</rfc>
