<?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.17 (Ruby 3.1.2) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-cfrg-schwabe-kyber-01" category="info" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.14.2 -->
  <front>
    <title abbrev="kyber">Kyber Post-Quantum KEM</title>
    <seriesInfo name="Internet-Draft" value="draft-cfrg-schwabe-kyber-01"/>
    <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="2022" month="September" day="23"/>
    <workgroup>None</workgroup>
    <keyword>kyber</keyword>
    <keyword>kem</keyword>
    <keyword>post-quantum</keyword>
    <abstract>
      <t>This memo specifies 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).
In essence, a KEM is a Public-Key Encryption (PKE) scheme where the
plaintext cannot be specified, but is generated as a random key as
part of the encryption. A KEM can be transformed into an unrestricted
PKE using HPKE <xref target="RFC9180"/>. On its own, a KEM can be used as a key agreement
method in TLS <xref target="hybrid"/>.</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.</t>
      <t>The public key consists of two values:</t>
      <ul spacing="normal">
        <li>
          <em>A</em> a uniformly sampled  k by k matrix over R <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
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 seed <em>rho</em>.</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 such that Compress after Decompress does nothing and</li>
        <li>d_u, d_v are scheme parameters.</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>
      </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.</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), d ) & ((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 R</name>
      <t>Kyber is defined over a polynomial ring R = GF(q)[x]/(x^n+1)
where n=256 (and q=3329). Elements of R 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="addition-and-multiplication">
          <name>Addition and multiplication</name>
          <t>Addition 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>where addition in each component is computed modulo q.</t>
          <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>
        <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="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 rigth; 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>
    </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 }     if i=j modulo 2
(NTT)_{ij} = [
             [ 0                                  otherwise
]]></artwork>
      <t>Its inverse is called the InvNTT.</t>
      <t>It can be computed more efficiently as described in section <xref target="S-NTT"/>.</t>
      <t>Examples:</t>
      <artwork><![CDATA[
NTT(1, 1, 0, ..., 0)   = (1, 1, ..., 1, 1)
NTT(1, 2, 3, ..., 255) = (2429, 2845, 425, 1865, ..., 2502, 2134, 2717, 2303)
]]></artwork>
      <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 anchor="dot-product-and-matrix-multiplication">
          <name>Dot product and matrix multiplication</name>
          <t>We will also use "o" to denote the dot product and matrix multiplication
in the NTT. 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>
    </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(msg) = SHAKE-256(msg)[:32]
H(msg) = SHA3-256(msg)
G(msg) = (SHA3-512(msg)[:32], SHA3-512(msg)[32:])
]]></artwork>
      <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(msg)       =  Keccak[512](msg || 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. Additionally, a smaller sponge
capacity is used for performance where permissable by the
security requirements.</t>
    </section>
    <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 always d &lt;= 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>
      <t>We define Decompress(-, d) for vectors and polynomials in the same way.</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})
]]></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})
]]></artwork>
          <t>DecodeVec(-, w) is the unique inverse of EncodeVec(-, w).</t>
        </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 2*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, i+offset), eta)
]]></artwork>
            <t>Recall that we start counting vector indices at zero.</t>
          </section>
        </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>n/8*12-octet tHatPacked</li>
              <li>32-octet rho</li>
            </ol>
          </li>
          <li>Parse tHat = DecodeVec(tHat, 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(Decode(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))</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(pk))</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>
      </references>
    </references>
    <section numbered="false" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>TODO acknowledge. (#16)</t>
    </section>
  </back>
  <!-- ##markdown-source:
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
HQHMBAzvQr0+9769aa3qnmxlk1vIhdEejOIcFHwWjaSsadfe3d7k5uzT8ETE
2M+kBcIbRmRWoChg38hSPStOzZMZV0zyPJkWYChNkzMNswR0r9SFKDUinAta
zjFnqL5Kkkm4dE7mmIkxnOegBS5wiQcUuGRtiqm2aBJbcif6LapOMNRj5xiG
CItmkv/fHldFETysD+0NgDOJICeCAgcLBIoiQxadlJV40w52Gs4mPuWUvzrG
9UDRW/pCHuEkms3gncXbsP5l6k/FOEmj70mc494pRrM9imZ7KprderJjcxRD
ZSjgajFXTYdTMGQVh5jgipaBQkM50s9q4HhMqTjonoSg8qLciGk/oZBpHk1V
ojx5ZUXkBYQ7hlcyEvoI65tEEdNBvCiBYzJRKbyYtGxhZtqk3gt2AtoE0K/A
3rD3wAYemHU7TmSrpC+fLXg022AAFWLL/GlozYycIM5ApnBZyYlHCAFxjtU+
OeGG19BF2PfP+LfEwN5oJMABxPh7LCb+96X4AOi6TKaYe0fuI4Lrr9AD7jQg
ehHU+pgbcGGcdyM/SOkAiNfzehtu/9kzOttR7CiU5wG+UEbpb7xhCCICppsm
CYV35zGmT8g9R1zRrvusSy2GIawtZC84avCsRcWzjjrIgpr5LcXfz+HR7T03
rFTOqmQdP4y+SVI8Qi/2qJwJwqH6QYMrcMr8dSdabKtHuLfiYuAcfn0TP6hS
BLPa/aaYzuMINHqe53fRtx9g8p82ddyrldV+EhSx6Htick1JfOAmCZKLpmTO
v6lSYzmEywZ2KcdaMagZta1sgENxC0Q6/FPB2R7KmF0hS6kIv9pmdZBMfflO
bSp4m2DrCW9ncwu0gwe/3J3tLV2nBy08tw9qw3vmPoPf/V7fpk2R6qZBTNgd
JRjYUPFW8sdZ1+mtEBSXlQAyEATQDnFT3cNSrMU9d4u4kI8Rd1gBQxox2/Jt
6Ai/DG27ixn5QUrh1AYaOqXg9BD+S+vxrkZKP6DKXbQOn0P5yUQlQlQSRE40
cRs6aaKmU6N9RNsEkdHTY8gLJek+sKrcFDA3sKrbTmrXgXIskZ3XkjWOEceJ
9G5Gj+qpAHkFgi6ntaC01km7cmNtQf3dFBgt4L0ACN0AeBbgeMA38oF412Rx
fnflACiwlL5ZliczZyoZsXsyRbU2LA2I+/TFeK096HDBKNmD3/DAHiBHCvdQ
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
HbT2Q7b2dc2wi11t29b/A+LKpFlijgAA

-->

</rfc>
