<?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-02" 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-02"/>
    <author fullname="Peter Schwabe">
      <organization>MPI-SPI &amp; Radboud University</organization>
      <address>
        <email>peter@cryptojedi.org</email>
      </address>
    </author>
    <author fullname="Bas Westerbaan">
      <organization>Cloudflare</organization>
      <address>
        <email>bas@cloudflare.com</email>
      </address>
    </author>
    <date year="2023" month="March" day="31"/>
    <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 ouput
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"/>.
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"/>.</t>
      <section anchor="warning-on-stability">
        <name>Warning on stability</name>
        <t><strong>NOTE</strong> This draft is not stable and does not (yet) match the final
NIST standard expected in 2024. 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, a) = Floor(x / a). <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></t>
      </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="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="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="hashToCurve"/> §2.2.5.
Additionally, a smaller sponge capacity is used for performance
where permissable by the security requirements.</t>
    </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="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="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"/>.</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 a 32-octet seed and deterministically
generates a shared secret and ciphertext for the public key as follows.</t>
        <ol spacing="normal" type="1"><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="decapsulation">
        <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>
      </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>
    </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.

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):
    # TODO #5 proper streaming SHAKE128
    return io.BytesIO(hashlib.shake_128(seed + bytes([j, i])).digest(
        length=1344))

def PRF(seed, nonce):
    # TODO #5 proper streaming SHAKE256
    assert len(seed) == 32
    return io.BytesIO(hashlib.shake_256(seed + bytes([nonce])
        ).digest(length=2000))

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 ifNew

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>TODO Security (#18)</t>
    </section>
    <section anchor="iana-considerations">
      <name>IANA Considerations</name>
      <t>TODO (#17)</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">
              <organization/>
            </author>
            <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">
              <organization/>
            </author>
            <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="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">
              <organization/>
            </author>
            <author fullname="K. Bhargavan" initials="K." surname="Bhargavan">
              <organization/>
            </author>
            <author fullname="B. Lipp" initials="B." surname="Lipp">
              <organization/>
            </author>
            <author fullname="C. Wood" initials="C." surname="Wood">
              <organization/>
            </author>
            <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="hashToCurve">
          <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>
      </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-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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
eypyWDEHbIeO3X1/l+7XuPysU22GLu3Qec+WjnEC7kkHd1PBm9LA70kBf0j6
9wNSv7XxN+Ib6drtHjCIZmXf7fZT4PKkp5/qPfwkcbxMGl/UBk1J5EsTyOvJ
4wvdLZAXk1aZdLrgIoXc0/Ymcqk8A+4FB2ABUTU+Tyq6x0y+xW7WyQ51fcrf
GcSP9LTTb3MZs4ZfwTSBFe92Tr2lyzHHMzuK9HKsR9bfTv5oS397gSYZOL0c
hza38RqHJO3+Dsfp9fuWDod088vwKjozp3WRd8ue1QkGoal2R/EFudatUpFy
ot5ub2Nz03hX5SnQJE2G0QOnRpRbx4o+QbyrNvoPmrU9OmxnzRM4LWFr56+n
3V9fXzfT1nlcMltsQDudb5zhZLDkeekefHdKl/oQ8SWf+5T+5GyqPdlQ6c2c
Sy47Qwtsgy1ro5fnNtJr2CidxD+j4VIfcZrX3cMpKFmcwKkr3RosKT0fTp3o
4r2Ix6kVj9OfW2OVPnSVe7s5SIsH6vj67Qe/Om1+q15vUlXfTkyiFXa1UTHw
tcuqqj7r1PqsNE2/8dgZs2jVn32AvwyQkLfc6ChXB/17FKUA2tYb3Q/rB6hJ
6dCpeX+XD1WSgP0UnvFCX+SC+L5ozxKeD/S0p46nPc3d6BGfSoyGC7EfZz4n
1flQberjVE/nxndiSOgMQa6rsjtWEDrCJPEvrwZELchPkna8k7T5aopTv65M
rnx3HG7SEE2zposdzAqDt/repCXyYJjXuOlIzjq4ycnlWUR9Cwm+ufEyrbBQ
GWkSUSK/s/RGVoQDRgjHLzXeCbjL2Lr5zpS9NjX5mV3uQBv/MZ2ulicY0J3a
onm3ILokxifpIpXvTdQzdEdG4NsNoNWnpJMxzXvypmBk1zy6K3/R0ZafBipx
TMsy0xPZmRVK0a9OThpPlGkrYjHaW5nU6eLwp5Xxlx36WJgNJ1lWz4DY0x9i
4IoxZk6CPBAKZtfnOJ5ERxF2T94nz69nIXax+IZEGub8+TV+v4uurWWit6f+
AbkmztYTn6vnoxqTWV7gGMUIwVN8IbFuOAqLMHD3WSuZKthKTeB16m8W5s2v
yjaW7ka+t5VTXeT7HnkPlm9ak0v4+bsKR5H5qnbd7BxuAtIHse9VGblbsZdo
xbCZQ7blJfaL9d8IzOp58x29hbNoJS/YYzYPvkyD5xd7DanHptuuzhpekvuu
K+lM5HqjJZnwlUamxcKAOiVa7BfpTooip8gkyRtjIDcpxfSXK0yvlmTEr9p0
+LxapcyBd5DSmtIE8yvfwQFyq1DKCcr3Y0IusrGIWMgPn16d7ARskZbQsBMg
Ab5LyzgJ2FZ9KL6aM8Z/gq77sseX4Sm6N5O87SzIJ61MrqlMT0CQOaicMR7p
yeCS08NL05OQKlE4pEu6ilRniPuuraOTxbGLCWgbZbLYJRLP5Rvqh1j9rLTA
9MxHM79r7nuwJdIAK58vNpgvNJhXaGkIl33Yd0gJeYD5Fa7hqdGQfM2L2aWl
kcmhNxCljxwZfqpn35EJDYuTHW52iv5O+KOmm1mF6IZNzIfVOmCsrXhuOxCb
qCQLLLqh3bzCy+XY+VUjqWteQyJ37mBtJlRSIs4vgd6zwO5p5vz7ZOO2fO3l
Vw7gRaMTy2TC8DaT0ulVO4+6a+t/viI2bpIZoDCSQqtUaVV9LXdG5fWDJbgl
fTdTWLKEM50evCpz0NRZSQNmQdXB9z67035j0j6pG249NKmeZgmgyDflNuwS
KmUw5STEOArlEuja2lMDTqnSVEPtqP5mQ/GqMteEXUrjpbWW97C9eVoieGkP
25vLe3i2LT1U03ZrgLDYIJbI+iZBd9Vk5w6rKbmMDa5Z7cAIiPvMojJkVfR5
CrbAYLJvUdlha+WcaQGSoV+p+9Wtpy0brvsPZV2/TAvii550QtK+/n4787V7
HCuyb1uPek99vlHz9d67vebKVAffMeMFQcB3SaP23hB3b4+j0QVfAyIHDsNZ
cYnr82746uhxfBXJudUwufL2u+qvNB11vBdjMr5wQO2g2/Fed9Uf8SyfhwTZ
jvevXbWfhTdkgo744QjfgTi86nhvcTNKOslx5QASzN/Ru9l4HM/DxNz6j4Ma
CTk3kpKe5zHQMuQzbmqfv0pR/ZFeeN5vqt3+8GJfPR/FRZo9xq59Ee202+pw
HCEJV76OXn9dmr7tZZrFGCTV6fl2cyOk3uQLiCpbizgwkg5nAIz59mWsd5SF
50WQDy9vwkEUDM+zi4Az8YL1HsFWvSBvx1wOWNyRG9RF6VtMRs8DxzaRrsIv
no/DQYpDDuaqhPJSMgYBXzWkr8/hWB1MW1RrPngb8NXV0S2cwbiwX5kO4Nrb
rqVOaDFfXiv28yWuo/kxUoeq6Wl83SmVchJdgfflcVmBCW6+IW85KWgw+YK/
SZyTdzi8xNXmUXETRUkV6LRQcytgFvHVbsP6Hed8nr7XBW/J/X1lLnGsv/GO
4GaTnfkqET6HodPJTL44xHtXtT5//YwjlnJSnq/HISz5VTQVcsrL/R5yc75b
7tLgw/8WO8j51Hjvev8PKKs8nuiYAAA=

-->

</rfc>
