<?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-03" 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-03"/>
    <author fullname="Peter Schwabe">
      <organization>MPI-SP &amp; Radboud University</organization>
      <address>
        <email>peter@cryptojedi.org</email>
      </address>
    </author>
    <author initials="B. E." surname="Westerbaan" fullname="Bas Westerbaan">
      <organization>Cloudflare</organization>
      <address>
        <email>bas@cloudflare.com</email>
      </address>
    </author>
    <date year="2023" month="September" day="24"/>
    <workgroup>None</workgroup>
    <keyword>kyber</keyword>
    <keyword>kem</keyword>
    <keyword>post-quantum</keyword>
    <abstract>
      <t>This memo specifies a preliminary version ("draft00", "v3.02")
    of Kyber, an IND-CCA2 secure Key Encapsulation Method.</t>
    </abstract>
    <note removeInRFC="true">
      <name>About This Document</name>
      <t>
        The latest revision of this draft can be found at <eref target="https://bwesterb.github.io/draft-schwabe-cfrg-kyber/draft-cfrg-schwabe-kyber.html"/>.
        Status information for this document may be found at <eref target="https://datatracker.ietf.org/doc/draft-cfrg-schwabe-kyber/"/>.
      </t>
      <t>Source for this draft and an issue tracker can be found at
        <eref target="https://github.com/bwesterb/draft-schwabe-cfrg-kyber"/>.</t>
    </note>
  </front>
  <middle>
    <section anchor="introduction">
      <name>Introduction</name>
      <t>Kyber is NIST's pick for a post-quantum key agreement <xref target="NISTR3"/>.</t>
      <t>Kyber is not a Diffie-Hellman (DH) style non-interactive key agreement,
but instead, Kyber is a Key Encapsulation Method (KEM).
A KEM is a three-tuple of algorithms (<em>KeyGen</em>, <em>Encapsulate</em>, <em>Decapsulate</em>):</t>
      <ul spacing="normal">
        <li>
          <em>KeyGen</em> takes no inputs and generates a private key and a public key;</li>
        <li>
          <em>Encapsulate</em> takes as input a public key and produces as output
a ciphertext and a shared secret;</li>
        <li>
          <em>Decapsulate</em> takes as input a ciphertext and a private key and
produces a shared secret.</li>
      </ul>
      <t>Like DH, a KEM can be used as an unauthenticated key-agreement
protocol, for example in TLS <xref target="HYBRID"/> <xref target="XYBERTLS"/>.
However, unlike DH, a KEM-based key agreement is <em>interactive</em>,
because the party executing Encapsulate can compute its protocol
message (the ciphertext) only after having received the input
(public key) from the party running <em>KeyGen</em> and <em>Decapsulate</em>.</t>
      <t>A KEM can be transformed into a PKE scheme using HPKE <xref target="RFC9180"/> <xref target="XYBERHPKE"/>.</t>
      <section anchor="warning-on-stability-and-relation-to-ml-kem">
        <name>Warning on stability and relation to ML-KEM</name>
        <t><strong>NOTE</strong> This draft is not stable and does not (yet) match the final
NIST standard ML-KEM (FIPS 203) expected in 2024. It also does not
match the draft for ML-KEM published by NIST August 2023. <xref target="MLKEM"/></t>
        <t>Currently it matches Kyber as submitted
to round 3 of the NIST PQC process <xref target="KYBERV302"/>.</t>
      </section>
    </section>
    <section anchor="conventions-and-definitions">
      <name>Conventions and Definitions</name>
      <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL
NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
"<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they
appear in all capitals, as shown here.</t>
    </section>
    <section anchor="overview">
      <name>Overview</name>
      <t>Kyber is an IND-CCA2 secure KEM. It is constructed by applying a
Fujisaki-Okamato style transformation on InnerPKE, which is
the underlying IND-CPA secure Public Key Encryption scheme.
We cannot use InnerPKE directly, as its ciphertexts are malleable.</t>
      <artwork><![CDATA[
                   F.O. transform
   InnerPKE   ---------------------->   Kyber
   IND-CPA                              IND-CCA2
]]></artwork>
      <t>Kyber is a lattice-based scheme. More precisely, its security
is based on the learning-with-errors-and-rounding problem in module
lattices (MLWER).
The underlying polynomial ring R (defined in <xref target="S-ring"/>) is chosen such that
multiplication is very fast using the number theoretic transform
(NTT, see <xref target="S-NTT"/>).</t>
      <t>An InnerPKE private key is a vector <em>s</em> over R of length k which is
<em>small</em> in a particular way. Here <tt>k</tt> is a security parameter akin to the
size of a prime modulus. For Kyber512, which targets AES-128's security level,
the value of k is 2, for Kyber768 (AES-192 security level) k is 3,
and for Kyber1024 (AES-256 security level) k is 4.</t>
      <t>The public key consists of two values:</t>
      <ul spacing="normal">
        <li>
          <em>A</em> a k-by-k matrix over R sampled uniformly at random <em>and</em></li>
        <li>
          <em>t = A s + e</em>, where <tt>e</tt> is a suitably small masking vector.</li>
      </ul>
      <t>Distinguishing between such A s + e and a uniformly sampled t is the
decision module learning-with-errors (MLWE) problem. If that is hard, then
it is also hard to recover the private key from the public key
as that would allow you to distinguish between those two.</t>
      <t>To save space in the public key, A is recomputed deterministically from
a 256bit seed <em>rho</em>. Strictly speaking, A is not uniformly random anymore,
but it's computationally indistuinguishable from it.</t>
      <t>A ciphertext for a message m under this public key is a pair (c_1, c_2)
computed roughly as follows:</t>
      <artwork><![CDATA[
c_1 = Compress(A^T r + e_1, d_u)
c_2 = Compress(t^T r + e_2 + Decompress(m, 1), d_v)
]]></artwork>
      <t>where</t>
      <ul spacing="normal">
        <li>e_1, e_2 and r are small blinds;</li>
        <li>Compress(-, d) removes some information, leaving d bits per coefficient
and Decompress is an "approximate inverse" of Compress;</li>
        <li>d_u, d_v are scheme parameters; and</li>
        <li>superscript T denotes transposition, so <em>A^T</em> is the transpose of A,
see <xref target="transpose"/> and <em>t^T r</em> is the dot product
of t and r, see <xref target="dot-prod"/>.</li>
      </ul>
      <t>Distinguishing such a ciphertext and uniformly sampled (c_1, c_2)
is an example of the full MLWER problem, see Section 4.4 of <xref target="KYBERV302"/>.</t>
      <t>To decrypt the ciphertext, one computes</t>
      <artwork><![CDATA[
m = Compress(Decompress(c_2, d_v) - s^T Decompress(c_1, d_u), 1).
]]></artwork>
      <t>It it not straight-forward to see that this formula is correct.
In fact, there is negligable but non-zero probability that a ciphertext
does not decrypt correctly given by the DFP column in <xref target="params"/>.
This failure probability can be computed by a careful automated
analysis of the probabilities involved, see <tt>kyber_failure.py</tt> of <xref target="SECEST"/>.</t>
      <t>To define all these operations precisely, we first define the field
of coefficients for our polynomial ring; what it means to be small;
and how to compress. Then we define the polynomial ring R; its operations
and in particular the NTT. We continue with the different methods of
sampling and (de)serialization. Then, we first define InnerPKE
and finally Kyber proper.</t>
    </section>
    <section anchor="the-field-gfq">
      <name>The field GF(q)</name>
      <t>Kyber is defined over GF(q) = Z/qZ, the integers modulo q = 13*2^8+1 = 3329.</t>
      <section anchor="size">
        <name>Size</name>
        <t>To define the size of a field element, we need a signed modulo. For any odd m,
we write</t>
        <artwork><![CDATA[
a smod m
]]></artwork>
        <t>for the unique integer b with -(m-1)/2 &lt; b &lt;= (m-1)/2 and b = a modulo m.</t>
        <t>To avoid confusion, for the more familiar modulo we write umod; that is</t>
        <artwork><![CDATA[
a umod m
]]></artwork>
        <t>is the unique integer b with 0 &lt;= b &lt; m and b = a modulo m.</t>
        <t>Now we can define the norm of a field element:</t>
        <artwork><![CDATA[
|| a || = abs(a smod q)
]]></artwork>
        <t>Examples:</t>
        <artwork><![CDATA[
 3325 smod q = -4        ||  3325 || = 4
-3320 smod q =  9        || -3320 || = 9
]]></artwork>
        <t><cref anchor="_4" source="Bas">TODO (#23) Should we define smod and umod at all, since we don't
use it.</cref></t>
      </section>
      <section anchor="compression">
        <name>Compression</name>
        <t>In several parts of the algorithm, we will need a method to compress
fied elements down into d bits. To do this, we use the following method.</t>
        <t>For any positive integer d, integer x and integer 0 &lt;= y &lt; 2^d, we define</t>
        <artwork><![CDATA[
  Compress(x, d) = Round( (2^d / q) x ) umod 2^d
Decompress(y, d) = Round( (q / 2^d) y )
]]></artwork>
        <t>where Round(x) rounds any fraction to the nearest integer going up with ties.</t>
        <t>Note that in <xref target="S-VectorOps"/> we extend Compress and Decompress
to polynomials and vectors of polynomials.</t>
        <t>These two operations have the following properties:</t>
        <ul spacing="normal">
          <li>
            <tt>0 &lt;= Compress(x, d) &lt; 2^d</tt></li>
          <li>
            <tt>0 &lt;= Decompress(y, d) &lt; q</tt></li>
          <li>
            <tt>Compress(Decompress(y, d), d) = y</tt></li>
          <li>If <tt>Decompress(Compress(x, d), d) = x'</tt>, then <tt>|| x' - x || &lt;= Round(q/2^(d+1))</tt></li>
          <li>If <tt>x = x' modulo q</tt>, then <tt>Compress(x, d) = Compress(x', d)</tt></li>
        </ul>
        <t>For implementation efficiency, these can be computed as follows.</t>
        <artwork><![CDATA[
  Compress(x, d) = Div( (x << d) + q/2), q ) & ((1 << d) - 1)
Decompress(y, d) = (q*y + (1 << (d-1))) >> d
]]></artwork>
        <t>where Div(x, 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="symmetric-cryptographic-primitives">
      <name>Symmetric cryptographic primitives</name>
      <t>Kyber makes use of various symmertic primitives PRF, XOF, KDF, H and G, where</t>
      <artwork><![CDATA[
XOF(seed) = SHAKE-128(seed)
PRF(seed, counter) = SHAKE-256(seed || counter)
KDF(prekey) = SHAKE-256(msg)[:32]
H(msg) = SHA3-256(msg)
G(msg) = (SHA3-512(msg)[:32], SHA3-512(msg)[32:])
]]></artwork>
      <t>Here <tt>counter</tt> is an octet; <tt>seed</tt> is 32 octets; <tt>prekey</tt> is 64 octets;
and the length of <tt>msg</tt> varies.</t>
      <t>On the surface, they look different, but they are all based on
the same flexible Keccak XOF that uses the f1600 permutation,
see <xref target="FIPS202"/>:</t>
      <artwork><![CDATA[
XOF(seed)      =  Keccak[256](seed || 1111, .)
PRF(seed, ctr) =  Keccak[512](seed || ctr || 1111, .)
KDF(prekey)    =  Keccak[512](prekey || 1111, 256)
H(msg)         =  Keccak[512](msg || 01, 256)
G(msg)         = (Keccak[1024](msg || 01, 512)[:32],
                  Keccak[1024](msg || 01, 512)[32:])

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

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

import io
import hashlib
import functools
import collections

from math import floor

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

def Dec(sk, ct, params):
    sk2 = sk[:12 * params.k * n//8]
    pk = sk[12 * params.k * n//8 : 24 * params.k * n//8 + 32]
    h = sk[24 * params.k * n//8 + 32 : 24 * params.k * n//8 + 64]
    z = sk[24 * params.k * n//8 + 64 : 24 * params.k * n//8 + 96]
    m2 = InnerDec(sk, ct, params)
    Kbar2, r2 = G(m2 + h)
    ct2 = InnerEnc(pk, m2, r2, params)
    return constantTimeSelectOnEquality(
        ct2, ct,
        KDF(Kbar2 + H(ct)),  # if ct == ct2
        KDF(z + H(ct)),      # if ct != ct2
    )
]]></artwork>
    </section>
    <section anchor="security-considerations">
      <name>Security Considerations</name>
      <t>Kyber512, Kyber768 and Kyber1024 are designed to be post-quantum
IND-CCA2 secure KEMs, at the security levels of AES-128, AES-192 and AES-256.</t>
      <t>The designers of Kyber recommend Kyber768.</t>
      <t>The inner public key encryption <bcp14>SHOULD NOT</bcp14> be used directly,
as its ciphertexts are malleable.  Instead, for public key encryption,
HPKE can be used to turn Kyber into IND-CCA2 secure PKE <xref target="RFC9180"/> <xref target="XYBERHPKE"/>.</t>
      <t>Any implementation <bcp14>MUST</bcp14> use implicit rejection as specified in <xref target="S-decaps"/>.</t>
    </section>
  </middle>
  <back>
    <references>
      <name>References</name>
      <references>
        <name>Normative References</name>
        <reference anchor="FIPS202" target="https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf">
          <front>
            <title>FIPS PUB 202: SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions</title>
            <author initials="" surname="National Institute of Standards and Technology">
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
        <reference anchor="RFC2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author fullname="S. Bradner" initials="S." surname="Bradner"/>
            <date month="March" year="1997"/>
            <abstract>
              <t>In many standards track documents several words are used to signify the requirements in the specification. These words are often capitalized. This document defines these words as they should be interpreted in IETF documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>
        <reference anchor="RFC8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author fullname="B. Leiba" initials="B." surname="Leiba"/>
            <date month="May" year="2017"/>
            <abstract>
              <t>RFC 2119 specifies common key words that may be used in protocol specifications. This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the defined special meanings.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>
      </references>
      <references>
        <name>Informative References</name>
        <reference anchor="KYBERV302" target="https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf">
          <front>
            <title>CRYSTALS-Kyber, Algorithm Specification And Supporting Documentation (version 3.02)</title>
            <author initials="R." surname="Avanzi">
              <organization/>
            </author>
            <author initials="J." surname="Bos">
              <organization/>
            </author>
            <author initials="L." surname="Ducas">
              <organization/>
            </author>
            <author initials="E." surname="Kiltz">
              <organization/>
            </author>
            <author initials="T." surname="Lepoint">
              <organization/>
            </author>
            <author initials="V." surname="Lyubashevsky">
              <organization/>
            </author>
            <author initials="J." surname="Schanck">
              <organization/>
            </author>
            <author initials="P." surname="Schwabe">
              <organization/>
            </author>
            <author initials="G." surname="Seiler">
              <organization/>
            </author>
            <author initials="D." surname="Stehle">
              <organization/>
            </author>
            <date year="2021"/>
          </front>
        </reference>
        <reference anchor="MLKEM" target="https://csrc.nist.gov/pubs/fips/203/ipd">
          <front>
            <title>FIPS 203 (Initial Draft): Module-Lattice-Based Key-Encapsulation Mechanism Standard</title>
            <author initials="" surname="National Institute of Standards and Technology">
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
        <reference anchor="SECEST" target="https://github.com/pq-crystals/security-estimates">
          <front>
            <title>CRYSTALS security estimate scripts</title>
            <author initials="L." surname="Ducas">
              <organization/>
            </author>
            <author initials="J." surname="Schanck">
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
        <reference anchor="RFC9180">
          <front>
            <title>Hybrid Public Key Encryption</title>
            <author fullname="R. Barnes" initials="R." surname="Barnes"/>
            <author fullname="K. Bhargavan" initials="K." surname="Bhargavan"/>
            <author fullname="B. Lipp" initials="B." surname="Lipp"/>
            <author fullname="C. Wood" initials="C." surname="Wood"/>
            <date month="February" year="2022"/>
            <abstract>
              <t>This document describes a scheme for hybrid public key encryption (HPKE). This scheme provides a variant of public key encryption of arbitrary-sized plaintexts for a recipient public key. It also includes three authenticated variants, including one that authenticates possession of a pre-shared key and two optional ones that authenticate possession of a key encapsulation mechanism (KEM) private key. HPKE works for any combination of an asymmetric KEM, key derivation function (KDF), and authenticated encryption with additional data (AEAD) encryption function. Some authenticated variants may not be supported by all KEMs. We provide instantiations of the scheme using widely used and efficient primitives, such as Elliptic Curve Diffie-Hellman (ECDH) key agreement, HMAC-based key derivation function (HKDF), and SHA2.</t>
              <t>This document is a product of the Crypto Forum Research Group (CFRG) in the IRTF.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="9180"/>
          <seriesInfo name="DOI" value="10.17487/RFC9180"/>
        </reference>
        <reference anchor="NISTR3" target="https://csrc.nist.gov/News/2022/pqc-candidates-to-be-standardized-and-round-4">
          <front>
            <title>PQC Standardization Process: Announcing Four Candidates to be Standardized, Plus Fourth Round Candidates</title>
            <author initials="" surname="The NIST PQC Team">
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
        <reference anchor="HYBRID">
          <front>
            <title>Hybrid key exchange in TLS 1.3</title>
            <author fullname="Douglas Stebila" initials="D." surname="Stebila">
              <organization>University of Waterloo</organization>
            </author>
            <author fullname="Scott Fluhrer" initials="S." surname="Fluhrer">
              <organization>Cisco Systems</organization>
            </author>
            <author fullname="Shay Gueron" initials="S." surname="Gueron">
              <organization>University of Haifa and Amazon Web Services</organization>
            </author>
            <date day="12" month="February" year="2020"/>
            <abstract>
              <t>   Hybrid key exchange refers to using multiple key exchange algorithms
   simultaneously and combining the result with the goal of providing
   security even if all but one of the component algorithms is broken.
   It is motivated by transition to post-quantum cryptography.  This
   document provides a construction for hybrid key exchange in the
   Transport Layer Security (TLS) protocol version 1.3.

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

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-stebila-tls-hybrid-design-03"/>
        </reference>
        <reference anchor="H2CURVE">
          <front>
            <title>Hashing to Elliptic Curves</title>
            <author fullname="Armando Faz-Hernandez" initials="A." surname="Faz-Hernandez">
              <organization>Cloudflare, Inc.</organization>
            </author>
            <author fullname="Sam Scott" initials="S." surname="Scott">
              <organization>Cornell Tech</organization>
            </author>
            <author fullname="Nick Sullivan" initials="N." surname="Sullivan">
              <organization>Cloudflare, Inc.</organization>
            </author>
            <author fullname="Riad S. Wahby" initials="R. S." surname="Wahby">
              <organization>Stanford University</organization>
            </author>
            <author fullname="Christopher A. Wood" initials="C. A." surname="Wood">
              <organization>Cloudflare, Inc.</organization>
            </author>
            <date day="15" month="June" year="2022"/>
            <abstract>
              <t>This document specifies a number of algorithms for encoding or hashing an arbitrary string to a point on an elliptic curve.  This document is a product of the Crypto Forum Research Group (CFRG) in the IRTF.
              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-irtf-cfrg-hash-to-curve-16"/>
        </reference>
        <reference anchor="XYBERHPKE">
          <front>
            <title>X25519Kyber768Draft00 hybrid post-quantum KEM for HPKE</title>
            <author fullname="Bas Westerbaan" initials="B." surname="Westerbaan">
              <organization>Cloudflare</organization>
            </author>
            <author fullname="Christopher A. Wood" initials="C. A." surname="Wood">
              <organization>Cloudflare</organization>
            </author>
            <date day="4" month="May" year="2023"/>
            <abstract>
              <t>   This memo defines X25519Kyber768Draft00, a hybrid post-quantum KEM,
   for HPKE (RFC9180).  This KEM does not support the authenticated
   modes of HPKE.

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

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-tls-westerbaan-xyber768d00-02"/>
        </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-02">
        <name>Since draft-schwabe-cfrg-kyber-02</name>
        <ul spacing="normal">
          <li>Fix a typo in the machine-readable specification, and use a proper
SHAKE implementation. #5</li>
          <li>Add table with sizes.</li>
          <li>Reordered sections.</li>
          <li>Add reference to Kyber in HPKE.</li>
          <li>Miscellaneous editorial changes.</li>
          <li>Remove encapsulation seed as an explicit parameter in the written
specification.</li>
          <li>Write security recommendations. #18</li>
          <li>Explain relation with ML-KEM.</li>
        </ul>
      </section>
      <section anchor="since-draft-schwabe-cfrg-kyber-01">
        <name>Since draft-schwabe-cfrg-kyber-01</name>
        <ul spacing="normal">
          <li>Fix various typos.</li>
          <li>Move sections around.</li>
          <li>Elaborate domain separation and encoding of nonces
in symmetric primitives.</li>
          <li>Add explicit formula for InvNTT.</li>
          <li>Add acknowledgements.</li>
        </ul>
      </section>
      <section anchor="since-draft-schwabe-cfrg-kyber-00">
        <name>Since draft-schwabe-cfrg-kyber-00</name>
        <ul spacing="normal">
          <li>Test specification against NIST test vectors.</li>
          <li>
            <t>Fix two unintentional mismatches between this document
and the reference implementation:  </t>
            <ol spacing="normal" type="1"><li>KDF uses SHAKE-256 instead of SHAKE-128.</li>
              <li>Reverse order of seed. (<tt>z</tt> comes at the end.)</li>
            </ol>
          </li>
          <li>Elaborate text in particular introduction, and symmetric key section.</li>
        </ul>
      </section>
    </section>
  </back>
  <!-- ##markdown-source:
H4sIAMiMEGUAA8V9+1bbSJfv/3qKGrJmYhvLYHMJoZueoYFcJjcG6O75hhAi
2wIUbMlIsoFc5lnOf+c95snO/u1dVSrJMuGbmbUOK7Etqa77vnftKvm+7+VR
Pgq31Zv7fpiqwyTL/X+bBnE+Has3B++8oN9Pw9m2usZjbxDk4WWS3m+rKL5I
PG+YDOJgTLWHaXCR+4OL9NLPBle3QT/0uYa/uuYNqdK2l+VBPDwPRklMxe/D
zKNG19QTdfJh/4P3RAVpGGyr3aODXXPvNkmvL9NkOtlW76mSdx3e063htqd8
PRr8CMf4mmDYNzJsbxbGU+pQKV1778XRS7rK7yfU81/UahRfqpd4Rv2qcRCN
6PbLfwnvgvFkFHYGyVgV46KBDa621VWeT7LtlRWn0MpfL00x6irKr6b9bbXU
vw2zPEz7KwIRAwyGDI96iUqPCCJZTqVNs6ZWR9rpRMnC+iuLQN25ysejJc8L
pvlVkgJM1JNSF9PRSHB0GFIX6ljq8LMkvQzi6GuQR0m8rd4dvvaPD9U/qaNg
2E+mQ/VHHM3CNIvyey4dCqgmaOZfBun9JE++hMOoQ614prcozrbV752DjvpL
ZhQEcWUYvwdZ9WF5HHsj6vxiRBThdtsPsn8Z2CdAgOfFSTqmSjPG9ovXh8e9
1d42V8qD9DLMC7zFs9Fk2s86cZTlnctktoIfuLNyEU0yvurgV4da6EyGF9KI
cMZTtKwO//hdoXl1/GrXX1PHIOeAyJHAmo6nOY/dp7mFQ/UqyK4UPVYHd3lI
pfqj0P8wpe5y9WIaD1Aye8o9WFzxn6+/NRifvudGg5F6HWc0lmkequTC9pxx
Fyfh4CpORsnl/VPPA1M6EHnzt98Pjv5cWwSTyY1PWCS+HGXA4YomryAP5Kef
TcJBdBENZG7EMPFwzScYdFe3VterUNo7+tvxye7bY58FSVvtjkhQEDWP1bHb
jNqlQR9PJ5MkzcGH+8lgOg5jgZ9qML3Rj7XOaq/5cxAdddTuLIi/RvWP/7Wj
fk+y+mdvO2p/OggWPCUCfhON8q/1T0866m04SaI4r3/+Jz2/nxLFXoWz7Pp+
4eCIF4N4cF3//LBT4tW55y/peRiNIAfrHu/T4zy8GoVaSKlpHA2SYUhPVRpe
hGkYD0KZPQtoEHdXWJVpyED8cP/F/zLJvHtLiqWeJAdZOih4tODP3uraSjQZ
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
7ARQa2QHNrMwpT51TFdGNg8SY6mJVRKJQhDTl9BEQ0S8gCOzDDj18kXjpukY
x8beZJ3KD4lz/mPl5j/aOgaSh5ckn0SXJ+qGnnbXPrZ6n7aWIevX1nrPJSJx
THaai1vULmw36TwcSZAQs4ihD8kiiS7RvTQvRh0pPJUM6V7bo3K3ZE+FwtRU
msqpsecB3eLtRDdTO0rVFxD7jbHfba701K9059cdZS4Boj4NOjCzGQs1BrMk
GgJNF9OMxa1pHnqXGGdMpE441ZXMmNSUbvxirBYzwqkeoZa49QNcxahoaGpc
P6b3RJW3EtFyoIll3Bpoat37/Ts9oI8dxLAbGlRA9YFIX6OjgbIN/ZQK++vG
c6K68owbkUUGn26sFoXVc6ewPOPCzz3v9NP6mXxuyzpa40lvramOr9j0KhiK
22LNwT8QIhqR5IhisrJQKomfymoh3FAYMBwA/8EkZgQ6h7xJvGaIKxJXgu+s
cLLRYqay24hEjCY14TWX3T0CpIUjYii3sUTlxB4gnkP0ikU8t2bCkGL1gFfH
JmZvCFd09qxAOclF8/NOiaCQKyaCeyKC3qdhu4CQcaOt9rpjG2ZHFmQaqkHF
1QphlpprChTpDldylNl9pdINVaFiTerQWFP62V1T4mQZD/+C466J8b4IdKQN
stwO+jLBrKcTLctI/jO55loZaq/1T/YnPkwyxJFC0v9Y37czqhhViNQVIlae
ikPCOHUeiR8lJrqrMa5gqJfxIrIP4wPZt9RnhnYFpgz6z8XjOfj9qm7kcZ0p
wUU0mO+5GPkwn50C5d50ybunn8W3UZ+Jde6ekh1yByb61eDqZqX3qTFc7jab
ts07rmdFsG1gjkSKG09x57OQZQT2L5IHjNYc3Le18q2aAYVd31lIjfvRjMjq
Tv36K66XFQ2bpnhDJPlPqtHo6vs+mVKLSLNx07qnilK2MSQR3Wyq335TQ0Of
6IK6C1D4xShJUupuhS476vRTl4VN1wibfRHLAWnUHKs8g9F0GBpbJblwLLXC
QvtnGVhilVFudReNAlT466//XIgf1qGi/2/Utyc6NLNIkwbzVgMEKCvY07uz
lcbdp5hwrGca7yB40ECfNztQqTTHAyOVaPhUl8O6WBHjGyg+p5o5iMcCb6AN
q3GJsbBmYxrl2DAWry8RvLtnP1EW3AKgJvh4vtpWnU6HwE+G/MYGnCigjyuX
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
UnwF612gAAA=

-->

</rfc>
