<?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 2.6.8) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-cfrg-schwabe-kyber-00" category="info" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.14.0 -->
  <front>
    <title abbrev="kyber">Kyber Post-Quantum KEM</title>
    <seriesInfo name="Internet-Draft" value="draft-cfrg-schwabe-kyber-00"/>
    <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="August" day="22"/>
    <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.</t>
      <t>TODO #7</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 Kyber.CPAPKE, which is
the underlying IND-CPA secure Public Key Encryption scheme.
We cannot use Kyber.CPAPKE directly, as its ciphertexts are malleable.</t>
      <artwork><![CDATA[
                   F.O. transform
Kyber.CPAPKE   ---------------------->   Kyber
   IND-CPA                              IND-CCA2
]]></artwork>
      <t>Kyber.CPAPKE is a lattice-based scheme. More precisely, its security
is based on the learning-with-errors problem in module lattices (MLWE).
The underlying polynomial ring R (defined in TODO) is chosen such that
multiplication is very fast using the number theoretic transform
(NTT, see TODO).</t>
      <t>A Kyber.CPAPKE 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
MLWE problem.</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>TODO add a quick rationale.</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>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 define first Kyber.CPAPKE
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 TODO we define Compress and Decompress for 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).</t>
        <t>TODO Do we want to include the proof that this is correct?
TODO Do we need to define &gt;&gt; and &lt;&lt;?</t>
      </section>
    </section>
    <section anchor="the-ring-r">
      <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 TODO.</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="background-on-the-number-theoretic-transform-ntt">
          <name>Background on the Number Theoretic Transform (NTT)</name>
          <t>TODO (#8) This section gives background not necessary for the implementation.
     Should we keep it?</t>
          <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 section TODO.</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.</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>TODO Montgomery, Barrett and https://eprint.iacr.org/2020/1377.pdf
TODO perhaps move this elsewhere?</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 TODO.</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="multiplication-in-ntt-domain">
        <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 cryptographic 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>TODO Elaborate on types and usage
TODO Stick to one?</t>
    </section>
    <section anchor="operations-on-vectors">
      <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>
      <t>TODO #20</t>
      <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,34) = (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="kybercpapke">
      <name>Kyber.CPAPKE</name>
      <t>We are ready to define the IND-CPA secure Public-Key Encryption scheme that
underlies Kyber.</t>
      <t>TODO warning about using Kyber.CPAPKE directly (#21)</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>TODO reference to table with values.</t>
      </section>
      <section anchor="key-generation">
        <name>Key generation</name>
        <t>Kyber.CPAPKE.KeyGen(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>Kyber.CPAPKE.Enc(msg, publicKey, seed) takes a 32-octet seed,
and deterministically encrypts the 32-octet msg for the Kyber.CPAPKE 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>Kyber.CPAPKE.Dec(cipherText, privateKey) takes a Kyber.CPAPKE 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 z</li>
              <li>A 32-octet cpaSeed</li>
            </ol>
          </li>
          <li>
            <t>Compute
            </t>
            <ol spacing="normal" type="1"><li>(cpaPublicKey, cpaPrivateKey) = Kyber.CPAPKE.KeyGen(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 = Kyber.CPAPKE.Enc(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 = Kyber.CPAPKE.Dec(cipherText, cpaPrivateKey)</li>
              <li>(KBar2, cpaSeed2) = G(m2 || h)</li>
              <li>cipherText2 = Kyber.CPAPKE.Enc(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 anchor="common-to-all-parameter-sets">
        <name>Common to all parameter sets</name>
        <table>
          <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>
          <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>
      </section>
      <section anchor="parameter-sets">
        <name>Parameter sets</name>
        <table>
          <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>
          <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>
            </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>
            </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>
            </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>
            </tr>
          </tbody>
        </table>
      </section>
    </section>
    <section anchor="machine-readable-implementation">
      <name>Machine-readable implementation</name>
      <t>TODO insert kyber.py automatically (#14)</t>
      <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_128(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 the 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 CPAPKE_KeyGen(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 CPAPKE_Enc(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
    v = tHat.DotNTT(rHat).InvNTT() + e2 + Poly(Decode(msg, 1)).Decompress(1)
    c1 = u.Compress(params.du).Encode(params.du)
    c2 = v.Compress(params.dv).Encode(params.dv)
    return c1 + c2

def CPAPKE_Dec(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 = CPAPKE_KeyGen(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 = CPAPKE_Enc(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 = CPAPKE_Dec(sk, ct, params)
    Kbar2, r2 = G(m2 + h)
    ct2 = CPAPKE_Enc(pk, m2, r2, params)
    if ct == ct2: # NOTE <- in production this must be done in constant time!
        return KDF(Kbar2 + H(ct))
    return KDF(z + H(ct))
]]></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="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>
      </references>
    </references>
    <section numbered="false" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>TODO acknowledge. (#16)</t>
    </section>
  </back>
  <!-- ##markdown-source:
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
0gdFoJC+SnDU85nUNlGYETfloWRTNjENPVKsNfaChBJQqD5+zVZKxkYUkhrs
JUN1j9HIsMnPCgjRQOZric9E1BLpiMKfi9cVMj0TN/y6bukhEEnNJYEdXorP
BoDdm4S8e/yZ1FssPgMD3z2GdesOWfmZmpKbDe9TM1h3HUe3eUf1tB7UDVQ4
oSh4jCWfmfsiFMIi1qeWr9GyLVdBlPahXnEDwwrqrmS6g2gB3HMnnj3D53UB
aOMQgfP+SzSbrizvwNK7igObN60lVGTYZgCK0nHE8+ciUGyIXUB3PgK/miRJ
Ct1twKMyLA5YI/qwngFTRvFoMg/k0pkmJPR+zpYgOcIp+pV/M6uS+Od62YC+
kdmePfubWrd43V21ZPk1PtUuL2Rnd+cbzbtPMUyjHEy8621tiyZ2cLOLS5fT
FS+VfgFkjzmeMkd9ic8IXVkByXEmzTWSNszUkhWYbK2zKCiDcc4rdJaXZDdT
84A3EN//eNFri263CwQG03NrC41KnCCqbNsfcoW56MF0+WAQ39GnJ+4+oYEL
bfDz1haWbG1BV3+gEjCoYxpNbeXfIS2kKwNmLvquhWbOwMvKwT6fxxSthon8
GqYJL/cfCgsIHh+JvSCgwBk3aLmrMGj1Emha0CYjZgczM85vwehDK2cuF9Km
XxCG6bIOhUNdOOTCXQKEd0MbnEoQQnGxrxAAFRj6YGnrnhUaJHPFFDfeVTxu
4mQYgDnXTbAaFslkjjBOYeOp7pAl55OwQVVxWrZ3Oy6tWwC6aqQfW9WR6hiL
HHELRwyCzaPFJxeekAk6xBwtrtc2YzOqnisZqNqGZ7ThFW1UAjwA06hrmSlv
siL1AbOgJEYFm0ghgLOSJJNhklyXGEYZAfNcaxIMZ3fBqsjy0A80g6oVnww1
zdztBwIhAgMhTruhIiFd5t4TaagacyuNBlOAVsksMNVxWxuGFYNs6t9dRPRM
H7h+zjO7ZRkkjMBtBjacYHBDRJfkggFWFIBTDEuCQxAS9xf+iHZDYx2ses/D
P9XDP7WHL1V389GOw7H7LGSz4ipCf3ZYNIjzFYcYPfcx3CTtYnst6zIrnIyT
+YSm5joMZ4Dz3zjaIsM4YEDe+tXIFqnYIFrAyDM27m5IQnhnhNw2jH/lLMXw
/2uY+yRoTGV8/OR6O2jKukIJsLiR2o/68QmqrUxqfzJNwGZCgk7AR8YQCnBe
Xnb3SFzX3cY8s9anY8NTkjNNkMDy6A/x947GqyQ7wD93hK6E2N50ZMG6Lvjp
KqqXp15tne1NBdH3HH5e18+1QVur0tNtuxI8/1gTqL492YrT5OWp+K5GsWW+
wsdVYWTSJ2ZV13ti1cVn08rlebzFJRtkh/iFyouNDHJNwRaKA/T15mk+ZoEu
fGgBznjIkW6AH0JRupSz3XN7+CNc/uQyLhSu/Oy58pk/JQy9BBj+7LnyWX72
uBK2Id/LZ/6sJb1sF5sWrvxkFFyJnytxcyVursTNJbK6/IOCSl7sQAzTRRNt
SPzsy89NR+o7XkuxCNySIATlELKV8qQDDlkD/qfo82VAP6D8XVeGHRQ9UXFh
5e1N2TDMulrl4AnnUTsX5iJLcy0nP+I5ATMSVoRI7TGOEgoZt8UY93XFcEnR
3/0xTiKILCYQUESSNeKUFBnI/3SeU7oA2Y1s91zHyS3Pc9mMJG0AxvRzu7RD
rA3WGNLzzn63bvAnqtaYdn/EPbTRZI9fFHyt1hRdXLC3cOQeU5QlsNTNQDlO
u7x7LOMMP9L4XVGyZ+RKkUZXYwpfTFFRhn4WhekAdPfjzFST2kTjHQS1Wsj1
c4/8vCTjgBFFEXG/Cz2CS59cxjr5I00d4VOSBpQ/ZLs/ejW3AsKw/GLcXJpp
SgHTHEjlQ98/STYrCr6h1eMRn/Wc7yxMRi3xw1qurNWR+qrU11Z9Lc/sS9YS
P6zVt/rq90t9bdfX2rT64lrih7W2oFZV9Wq93tE8aKKALF1LJG/bQoLriZ+o
9wTROC3MPr9sEqKNZTKFjE8D13AsVJqCDdrnguX6KlFBEuDxfPyLZVCWzU22
LtBxgcW9gfZPYctHMao0juiyRQPfKt66ya4g5OAcofCh10TuBq8gbGg0ZCjM
sDWyECP2ZItUNj/K+sIyBdpWsbYhhKMsf7OusG2CtlWsTQPLZ6ir+XS7tubT
bexVVw5BdeAOhN9oHF6S6ZXEmCMY+3IOwMvFWQAK2X5peDeDmVBbmkGRkdHW
UV+ecXAFZtgwpnXZpFJusqIGeDveTpuK3VLxU3u4elXV4weYbauOt4VUv0DW
tou3Sk0REp16JDp/AYlOPRJ28RY7W8ieqKrJzwKWQu7AtAPJ8Toci8syhrb9
mdg//XiBDCW3xLDf/dOLaAAqF9Znh0nrqzFHYohcoFdlANDW900Rn4c+U1yb
cdIjpfqLACGp/jamJOXzlPfAaf3wM/J74l13W6n4u3KawJ35hWHuy1C6AL8o
mBKULriDLwWMBXVvQpkw9yYaXGDhswKqhE8tVAWfGigDn7sSVB19OiWoOvp0
SlB19OmUoOroU0OWCn06Jag6+nRKUHX06ZSgSvQhRx55SRRuZJ/TBDLW825b
8PbQpkiTW84Oxj1xTMtDAxEjd6+U8pljHAMDiW2jvSeV9toChAkE3gW4Pre+
vdlY1TyZyqa0kAejHRglOaj4GtSTMqZdZ3d7k6uzS8OIiLGfSQuE95jIrEBV
wK5RQ7WsJDVPZgyY5HkyLchgockZYlki0OuntRC1RoS4oOEcY/u+2E+SSbjs
nM5x0384z2EVuMQhHlJAlFdTqEEWcQMWS+z+NS6dYKfHnRPoIiyqSfl/fVJW
RfCwPnQ2gM6kgjoRFHSwQKAqMnTRqb2IV5ZuwCANZxN/hBpp/wTHA0Wv6Qs5
hJNoNoN3Dd699a9SfyrGSRp9TeIct1s5DoMh0Tyayi1bQc5REQABJYtRjoyU
L475NlGTehgvLLQmE5UCCQaM38Dsnkm1FWwEtDr0rYZfs4HAhhaYVzudyFGp
Mz5b0mg+QQcqhJb507AxM9JAOIOTYmKWL40MA0wyVtvcRCMeg9oTeAeEuUqm
YQoM88IHXy3PCUqnygOPx3k38kcpJdJ6Pa+34fafPKEcWWoCDKixP8vIIONV
DIQrpLWJNgbsjsEJyShriDf3QDYBvzRJKF47jzErSO4P4hB23SddqjEMYTAh
e59RjUcrSh5t1Ebe11LXUIL1FB7d3lPDPOS0M15ch9EX6TAdo/d4LLQrSKYo
xd4HNTb4GTP2N9FkIznCvREXI+Hw64v4TkARYLX7RXG7xyFl9PguvkVfvoOt
fVbXcK9SVvlJULehzwf2my23uOuB/KFZt4sgFfazY7Js2VpJqSU3srRPDe02
QY3CPxVt7aFc7wpZSkX41THBQRv05Tu1QeBtgn0lvJ3NLdDIHvxyd7a3NEwP
anhuH1S198R9Ar/7vb5DGxzlDYCYJjZIMJbAS4reyUCtVAoCw/QDp5CwVB0Z
JTncWLeIvvgYMAekma44j03fgYbwy9ABL34fjOiUgpY1HHNGAeYh/JdG2rcK
43wHkG/ROnwO5SezkAhRFxPzEOIONFLHO2dG/Yii/JHR0s8wEyrKAxBMGdM3
95/Ku0Zq08CfZKyn1pI1jsTGiXQigp9qqSB5iYIuJ5ygMtaZjXJfbEHt3RYz
WtB7ARS6BfIswL6Hb+Rq8KbH4uLbdQdIgaX0rdHwZE5LKW1wT+bxVbqlDnEv
veivuQcNLnhK9uA3PLCjxfG4PVyoTN3VhOlwSAHeKju/6DXD9nljD1qo29sT
J8vpNAT4keCTPLDgzWDlKBRspjZjp/419Dlna30VsDg6ftUW//MD/Hp7AL/e
0NBfW54GvG1ieiFK7MmbvbcvMamTS+g9NEFPbcY4TAtAUPb0CjdY1EtONj54
1ZxmVzYkFpwN+t45gbwxAPr6PRsc6lWT3m25XlG3Leyyvjc4V5sqLyf+MEFf
nkJsy1nIyRNzzIxkiJMck/ByNKJ4LSu2UrGOTLNoNI5Dub9s5ATKnf8pLo2l
PCBxxxbDN6UvvU8BcCBumqnzEpNbfwlKGNMcXFeZcnJXRB8fKPfl654aqif/
itQWtkxLMzSrd1fCO/Dxg3KqJOBlRnSNJHg8uCF3ZPViYcVDOL0O/EPaFpTU
qW2h6AHtYVKpbZmaagm1EXiZSTWq0Z0huiRpRdFFhIU23KIKt5BwqLfkZpGR
cMF0QGzUEGxUMr0xAYYYZU+TKJppgOpMjtfjHXiwEvPsNHkR5ZlOcJpEGRk9
Cb0U9mbltwwYwkwjNJuAlUZaiA1pvXBLlMFapH7vAE1VAsZwydQrNUNkaTb9
i2b0/Hmf80lgEaJkqB1HZUUVS/6gphXXa/c3Sfx6ZAFoU0D/c+1y+ZbX7pcx
HfZSSU1BKOljZneBCkc58Ex6MDB6GbfaXMYd/jDNMdFzKZgqPtLj9mNrsrGj
CM3GuiaaJNaEdyo5qxwn2BDOW1woKkk+JgZER1hdb6PvlNAAX3Cd9fSDBw9G
9gcXUrvep2/42Vb5D2hUlGYJ6U9kQta8dUQ5Y5HtPRiLRImgeP0+MnbJ9yxx
wlZCapX8qpmf0eEE3sA2UnaBWNy5nHt+wHZRAm9x4iVyM2Tg2YWy+GYX32IY
GCDCaFGVHwzAaJwg5b7571LR7hXawTzhIBP77QEVQUwVoDSHpQyNZLiIkjnp
RWt80CMqDnN4oDV6jpZRfqIRloOJpR+AdAGy972o6f5cTfmjapGZ4pSoioj+
FFE1oMwBVmnM5VQKpPdv6sgCH8Koar7CSElDfayhOOhAhwnLZxMaeDYBJZvk
EAybNATvvAn2hCMPxqShcr+KJGtT6k7HaRgqMR6SvhzSYYAhHQZIMVLtB3QG
gvUzd4EbWgL8fUpbwl1GPK1sn8Eju9L1yJOcxzLRWaeTBdRJgJ0sIp/ZJKD4
dABF3ifXI+nn7CYXCnY4qwlfbcuzo2DJ4nlxTIxmrIDussPPweesKz5ccnYh
781ph+X5rrhhhK9icNMCsFHV8Dgsj5t33GS7wuWisn/RoKw5sAVjvZ/XBR9G
HC3zcRJLEQAVKydVMkKTO3AGml9HGTod5/oZFPAkFKcpn9kvflCbceUuzk2z
bycPBJiIMTzrneP+0tZ2qzk8c8/Ffwp3uwSHFOaXsEJtkoO03RqeeecWGK7Z
ZIOcBThh3vmgImDgQgVE1IFO+K/AjLIu7oDGQTOo5jpEpHiaowxUw66IBzAN
ZPGQ0oLS8lppE7KwmB8/dmjR7LtPwendfooerYdrHPi1XuEYt8XO5jb5zhy5
4ubekfxh9jlPeuu6hToPP6RotvbkYbDWcYszMVOw8FeLpeh7HSmY+uyQFEtr
BNV10MSoCdV0PMMeOfoN8BZtf+qn+cUpvkeOozMb2uw4IrrlzTeZMtKKWtJn
QkXT+oJPo2Qyn8btho1sEQPRm4T9TXOcLJa0YPBWFU+8iiEyJPRAPVEWsVEc
tRBVPmYlTB9A+W3aacNgcibzHxDPjJWU9OP2Mh1nIHtOh6HwpGNxKhtmFk8Z
gZmAPjp2opVYIy/paSn4SjXrtNFSQAMzYKFpDpmbic5oaCWGp/EzjcnF+hXz
0zCSkxYAq6XRkBIvgWMjDk2pxiT3jULsCieirpp4IUubHqVfuBse5krD90b1
CJzNoXtqvfHT1F+yPSgNB+9jiwL2mTIcOXvMyvbbf3GAFh4mjkj3oyhQRp8X
4aM0/PiBAziGtaeL4TcGG6qrfKcMVl8fCYBp4j80GO0YHaLdZNtbKRfcyYcn
23LvWXG8fh/3QWWxs7Khp3hKr68aclW9XgHCBfD7iW7UVmjEF2DfoQYrEn75
VJ863rqwFJlkHJLiku6C9XvqtzBHDGb7MkNR5U8WYzrqhFPfqm7hG9g0qRma
6rZsR7qRQAKKcvD7aF2+VGzyoCZQo5EZ3FY0xzokhr6pMmmWRlYjBXPrzo53
as+Oc1Imn2vWl1ao6P+tvCzCHyZzdTq59nC5aD7yeMbEkT6wiBjSURJ/wkgO
QzIr+EoLFqbSKRNdF8jdumk1BuIDmiAolBwhMQ5gw8KFdxXECPUrS2wppRs0
kATCGCrCHemNBXtLASAp3astKEcU7QTStrE4xuqwNrXaovUGf73GXzC9+PH2
4BW2+rufkpOQ6WhbETLD6tcI9M7nQHnp3PTAPO4N+FQWAtuihmW7WyThoz5r
y5BQTgG8VfQELIAELiINnx4ipBKlWYgss5CIoM4XG6fJfcojRg1dnCBq4blV
bBdPrmK7b2DdnaKHTeEGvMwDesRjlfbRWbUEteYsdSi+RWa0yqwpTrEqntR3
vhD70M4Wrd3smbPzgpx+FcYyFmddI9CFd6/BPuMQJRMNjRu1ZrfwRau1wkXh
ADVVAXrQAWhrNXHxLptcoCHTFiT/qPVec29djCAfkHoiXQLAe2/8XFtChonE
SSMAnum3ZZ3jUogEwfpdET4Edu00+l0Kbs1z3XPGPeN2ROao3nIuJKwSBlkn
kNBpbKIJg5asaoGPgyOpdw0XEpvAtcMRH+8/3gsYjGpcMlK5QqYqlI63hVlG
s3wbPkYfEhPVl3JLihz24nB+VwWKpGaz5xvKMbTbLvCl1Edr8o3VgTOHq1ZD
yM2zhOka0LDedrfvYKDO8Dozg04VVqFk9eI9Rp4UdeONnY8tV/WDVD3yR9dh
oKipUUAKwzOoXdz+5QksXH89HcgCP8d7qoe0zFMUtK9w3kc60V8L6BHvIeBm
lw/krwb0rh1w2JHLSoyaFoyaatzmxc4aDuPTKbArAqLjhxgp7BYFXM5MbUCV
bhOQwTHiFdfRBg1gNOIxFjyrA8V8YB+PxNMHVdiiCp6uQI6fFW0GRenIj8ZW
WaxY453iXQy73DMJEjZJfH4Q1vM5lDeLum1D3ApOX3VNSMOQTWZ/yey+REfQ
3RAGavWsbACYvIy0+di6/tgCppZcO1KT5NHrRemdp1latTEXu9XJwrkYqeCL
PQseT36lCrvhH+maA3MeJMNkZQEqKKPFqMSgeJXCAkxuyWcZ8xl+nTt1anMC
i6Gc3joGmaKrK209fX68ztCThyjzLJxc1i576l4YvVQ95OYre3nbcn/rp5le
mRO8V6ijr4r6Rtlo5p9AFZzTEu2a8Oqo0Mv4ZLDtrqhbuWVreprHAPfGaohm
afViZYLWLk4WGlL8jDpcMuaPr2rxKS5zU1uqoXXDm5JB4xYV3je2nZN6u0PO
KscHxz46wmBKouNNpqJ9fUtuX4xTnsMa9n1j7M8CMZpvh37aVtPGFsyUh/um
Obt2tLQAxL6prqoLr7Xs6pnzHlZ6ZqvaEKJRn/Cgd2lH+O0LP1VYWXUcR2lK
Y1J4ToKwdk5KNq6t9vB6Fg7i1ZC/fkkvuMcWE9cr60GT1QrRKeDWTSmy2bZv
CdlYrVaWMFZFbuqVJ6q8cthSWDAFkNvTk+hJtvCkNBQ8oVuqdEQcwU0cVbjC
08vtW9eYYE/PsDm9cp1960nIr7VQfQpdqyMsnZzOFaG7/xaruRiqNRlv18Id
k9QKYK9b1eRlllRXZ0z52Ct6VcX1WNASOGH373FLWNyDy4jXWd0Dj2IaFTvk
K37uG5wqO7gfWJ8P/0CtG65OF7vgp3al8To06U9X+4plLTz8KAjDK9pVsV3r
ci0+gHMv3CdcO65k7eHhO6vf+wbQQzvk9+KQpynyTWKowT8wbgAB/1yCFykn
JQTf6O86Q6Q8htclkC3XK4NQWKeNeatmygozH+bNAwiwo41Lqad7O0qiGEMw
ZzDIT3DFv/pjEPaneepf/IEuxbWBwQHIY5zJuwTkrXv//lFKjwW9DHGvYh5r
FPRY+6tRjx/0iFaoevwT0ZDPc76JRHXeoZvW4iDEHQeEwet3RCUworpc/JUu
FzVd/qjDLjHxvcXCAqf2HkmNv7x7JAH+WtzDQmnzGnMbcVzNrwGKkLwhUI5H
wMTB0kJfxL3bg1941dH9IcsbAj/Z3lHACEc1SsCHhwrY7XmbEnjTAnbh1xaW
/85iKt75Izx42kHTmyJN9nF5GZGK4gwIw9eVd2dLvOk5wevj2K5uPnI35d7A
I/HH3vH7w/evhTrw6QvromGkNaH4C23o+CohEl+R+S+bKZ3al3eTZhO546V3
hNI5bePqQ5y0yc24QBMJIB0l5tPYz8aTaGgWXc5j4PlkkpmFMrOdrwzBcnIe
YMxjXQ3vl+F3N/JuMXqIcQcCCvg0Pa0au7Bo0BOJ6K7wuFqMX1stqsB9xwva
5b1Zd52NDQ/JUOQwyEq03mL7BoZdvKo8oKtamo8Z4HFbNB9fw+/HwZx+L/A3
cu9jxzFbIibclQ/Na0QJHEb0Rd0efEEPc5NjIjhIx6hJHGnV7K+u6Zk1iT2t
mpuqpitrbpk1iy150KHNO2MTHiM4d+I/xY0uAYsnFc+Bhh2iob39nYrOrgEr
961TxbvHfHkV6JPyXVX6iip9a5XGSV19NSg3C3Wbl/ISonXR6245xkjsvMVK
XW60CdwhL+hqiTsHxkklRjPlS5KKhvyMRFbeDIYys8SvWH9FZ3SvFzYPnS2N
PpA9T5M/8GLp5hAPnld7wXSAYYYI3qKR2Sv3cJbNpwBwFq1/OYfWoZcv5Md9
oSvO8cKh5q1TTTRAkKgA6bVVR4jEeYEh4Sa3/soYSgwQgbOz5hCTJyJE1Cu3
Di3ycRGy47Lztjg7N6ggU6D82taHS3BcmyahTJRkht6OU5q4lc090NCOw5lt
qh2+36BoYW1tDWx4Uhqoe/lkiNx7gXd1PPr4cfcLMHWTD4+EQXMID3fOmTc4
7369jCaTJmkoum2rrcVxNIG5p5SPonNE6OICj8BeXDQxaAOuT7aLf/XCseWQ
AjqUQAOT6rRi8lNouUBgdkxYnY0ymy0MhpONcBJKo4SEHwQaB0roL2FgpqzQ
2SxUIpx/2yYO+BrNVA+yBezLKfcTh1eynwc6uOn4MrWXNsgY7VJD2Xz4Mwgj
AGiTDoGUkcny9EFkLtcInW8She9r5QbCm5/FgWZvVxPGboiChFUsaMIx6bRZ
oQH+TPxliPo8Fhsb8nCQ+vmKm872rSKcbsV1noNeq+Y50U2rvPltqpC4Q5WA
k1v0xanWlF2uw3pU/w4Xr+S2yTdhoAh+xRNrNzV3zihEDE3HOKkN9fWHkMAf
dLzB+28B+c6+wNQT/LljLXrlHwsU62PBueiI/Mf1DPj1VfBM9o2N3dI8VRLB
1AtaKMPLFYzxSJxM8NxeG09S8kYYPqi/MKL+RAIe3rKxxQy83nkrtoqrq0aM
aqueQYx5Yb5bxQ7WlMMyTGeVwMBwwFZrfaHJX0HRSFE0QopK1j/zWrQUfl09
IVhj3dWV8atVHQtqG3hwFlQM/y+LZ41kxkCHlcL5TMT/F2Sz8/+ObKKsrEtR
67AI/aSUob3f0rJmtOP8uIH1QrjJbQBOgP8PymvrT4nru/lEMUr9aoCmhoy8
ZrwuJQzXrru2BNMwEIb+agctXmUkq9l0lsXCA0eJRxvhLws9+RRKiqLz6nvP
fA8yVoEYYgtq4atrYuhZAHVt2FxqaZNaVaLViE9XIdLFIPDFp4sPvfpJN/QH
w9Fdiu6KGg/ygvZWmBuCB2wdDTrSZ4hGltljNWz4Lz9u2ryp/Wcalyb7gw0b
MGTpmc7V/5cJ4NJADui+olbLxZnHqkUfrdYO8sCZK5/+5TTy+tXr35JaXtt0
LasSmxaZqbUuqk+tbm9igph+j9NTcbJWTnJJz8SVNVQNrkku79kAujqnPzVA
j/A08FpURiLoVDgFpgLfD4ouVw5XnUNtC7A/IgMT+WfEHm3JS60l+2HgggL2
5lWPyhFMui/QiT380JQhsm429q/DC3W0FZNwycs9w84A/W4QXYVgLHAq8a7b
39w0HdziEGycxKPwT2AHQlA3efLE7a7oez+NvD5uq5EnZM5tspeH4vV6PXMo
MrmtGAHuxxsd9S/wcC0nwMmWnDKKY3kWd0znb4u2+VCvPkJhtaqO+NqNYi11
Wriunpwzs6IcV9/23X8PRw+67rOszmufoUCwbz4rK9wVhqXEkXJNugjDmnum
NXelodVGqtWWBPthcwdJ/idMmfJR/WdGvec/Y6UYIaem35V21NCR0QVhhxdm
OrwAaIOhSmJejTP8ZDwD6eKvD2sDGXZPf2WBZ6pr2ODHhP/J5Z0bNqB/3PSf
WdxxImYYsai0Ca6c4xSrfkHvPxEJmRmRkFlWjhjSMcpwVBvnM/A7s/GDGtDW
uUTvtpD8Im0LA53XdrO0WMnIIsdByz1iRbmQnMWtaBC36P6Fc6e8sKkUmKJP
qrYiuqrNM92xpWneyWtjbBKX4oQ1InnCBzjM7PDihCVtyJByxzNVZRksIoys
q/h3mtzySPHkFG4bPej0wCQ8pCdqr+8Qey2o9ie0BM4I/kfUulJRYccmmvXm
ba1+5PgvZ/LQPeKzhLePJLZlLHDzxQym1qEoM2cVDHir6IyUPGdgmDoeKhnT
RRIuJs5W+Ei+PjurPTgn7Zz6fQILlfMqKucVXFYdcanFjHJiV5x6YbOezUp5
BOZBUnCu0IWRpt+We2H1Zmut5aPT74vse/1yrybpWXXRvS7gVqTeS0CZB11X
cUUyvlVR1artWCZmszVQNMvFYalY5eyrpTRTCc7wqYFm1yuS9Nd1hj71bIMV
qfml+W7OAPHsujprmOGF7yiJ+sdzx/eyWFNXyWCfXZ8NOmQdFjSzUAL9tgsD
POuQ7fhnZ7k+y/0nJvmhrPeHZjd8MAu+ZQzSgUUOvNICVSZNWmKAOc0+lCgO
oBT3wvwDViiCgbh7jCRWyrQKi1nx5ool8+Ed0wBxDRcQ6TAvzB6JfzB3uura
B11SVEIaLKqVFpVKiwrvjdB3H3kV1sMsxgzINspreI7/LIDaVAdcwMdXlIav
FNzeMcbUZhRH+dmAqp5ju2f01eCxucWoozqxRjoYhKuhxUI3wqZHwT5Ijpq6
i4qmKHDIrleKiZRczFTPDAaYMzcVPOAU0+Lq6XANUf/zmnl7U7/+SvHDMDiz
hJXVCf3Rx4r2l66h7MjyMikZeYVuQjYGrbcOgOviq71VzSB/amXR78vp0vjD
6dKpzJNeN5OkiZ3yYmBaQQJ8dVBvVeIrNEjtjHKnOkDk8Lf2bvkDnE9kzUCJ
UrDLZPqNjR1zChisDkoMhLdZU7wu+kZkbMwNrIRc3cr25rnNICtb2d5c3crT
7aKVqcFLNeSxZg4ELvVUMvO6ymTmifNqZo7gq03hJj0nEefeQDzCv2L9Ujzr
0B+ULLK46JKt6TzL8YLFALfzy/lZ/1FnZCqm8FZyBWdB67d04ZQ8a7ov/xyN
+htPfHWZett85O449MewD/fe79UDAwz+4QP64/R4pTpC743wnthJGFzRRSSN
bwNOpAiD3bVLf5KFa9/VHyzVkGEXm9p2Gv8HTf1+BpOEAAA=

-->

</rfc>
