/*
 * Copyright (c) 2012-2020 MIRACL UK Ltd.
 *
 * This file is part of MIRACL Core
 * (see https://github.com/miracl/core).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* Kyber API implementation. Constant time where it matters. Spends nearly all of its time running SHA3. Small.

   M.Scott 22/11/2021
*/

#include "kyber.h"

using namespace core;

// parameters for each security level
// K,eta1,eta2,du,dv,shared secret
const int PARAMS_512[6]={2,3,2,10,4,32};
const int PARAMS_768[6]={3,2,2,10,4,32};
const int PARAMS_1024[6]={4,2,2,11,5,32};

/* Start of public domain reference implementation code - taken from https://github.com/pq-crystals/kyber */

const sign16 zetas[128] = {
  -1044,  -758,  -359, -1517,  1493,  1422,   287,   202,
   -171,   622,  1577,   182,   962, -1202, -1474,  1468,
    573, -1325,   264,   383,  -829,  1458, -1602,  -130,
   -681,  1017,   732,   608, -1542,   411,  -205, -1571,
   1223,   652,  -552,  1015, -1293,  1491,  -282, -1544,
    516,    -8,  -320,  -666, -1618, -1162,   126,  1469,
   -853,   -90,  -271,   830,   107, -1421,  -247,  -951,
   -398,   961, -1508,  -725,   448, -1065,   677, -1275,
  -1103,   430,   555,   843, -1251,   871,  1550,   105,
    422,   587,   177,  -235,  -291,  -460,  1574,  1653,
   -246,   778,  1159,  -147,  -777,  1483,  -602,  1119,
  -1590,   644,  -872,   349,   418,   329,  -156,   -75,
    817,  1097,   603,   610,  1322, -1285, -1465,   384,
  -1215,  -136,  1218, -1335,  -874,   220, -1187, -1659,
  -1185, -1530, -1278,   794, -1510,  -854,  -870,   478,
   -108,  -308,   996,   991,   958, -1460,  1522,  1628
};

static int16_t montgomery_reduce(int32_t a)
{
  int16_t t;

  t = (int16_t)a*KY_QINV;
  t = (a - (int32_t)t*KY_PRIME) >> 16;
  return t;
}

static int16_t barrett_reduce(int16_t a) {
  int16_t t;
  const int16_t v = ((1<<26) + KY_PRIME/2)/KY_PRIME;

  t  = ((int32_t)v*a + (1<<25)) >> 26;
  t *= KY_PRIME;
  return a - t;
}

static sign16 fqmul(sign16 a, sign16 b) {
  return montgomery_reduce((sign32)a*b);
}

static void ntt(int16_t r[256]) {
  unsigned int len, start, j, k;
  int16_t t, zeta;

  k = 1;
  for(len = 128; len >= 2; len >>= 1) {
    for(start = 0; start < 256; start = j + len) {
      zeta = zetas[k++];
      for(j = start; j < start + len; j++) {
        t = fqmul(zeta, r[j + len]);
        r[j + len] = r[j] - t;
        r[j] = r[j] + t;
      }
    }
  }
}

static void invntt(int16_t r[256]) {
  unsigned int start, len, j, k;
  int16_t t, zeta;
  const int16_t f = 1441; // mont^2/128

  k = 127;
  for(len = 2; len <= 128; len <<= 1) {
    for(start = 0; start < 256; start = j + len) {
      zeta = zetas[k--];
      for(j = start; j < start + len; j++) {
        t = r[j];
        r[j] = barrett_reduce(t + r[j + len]);
        r[j + len] = r[j + len] - t;
        r[j + len] = fqmul(zeta, r[j + len]);
      }
    }
  }

  for(j = 0; j < 256; j++)
    r[j] = fqmul(r[j], f);
}

static void basemul(sign16 r[2], const sign16 a[2], const sign16 b[2], sign16 zeta) {
    r[0]  = fqmul(a[1], b[1]);
    r[0]  = fqmul(r[0], zeta);
    r[0] += fqmul(a[0], b[0]);
    r[1]  = fqmul(a[0], b[1]);
    r[1] += fqmul(a[1], b[0]);
}

static void poly_reduce(sign16 *r)
{
    int i;
    for(i=0;i<KY_DEGREE;i++)
        r[i] = barrett_reduce(r[i]);
}

static void poly_ntt(sign16 *r)
{
    ntt(r);
    poly_reduce(r);
}

static void poly_invntt(sign16 *r)
{
    invntt(r);
}

// Note r must be distinct from a and b
static void poly_mul(sign16 *r, const sign16 *a, const sign16 *b)
{
    int i;
    for(i = 0; i < KY_DEGREE/4; i++) {
        basemul(&r[4*i], &a[4*i], &b[4*i], zetas[64 + i]);
        basemul(&r[4*i + 2], &a[4*i + 2], &b[4*i + 2], -zetas[64 + i]);
    }
}

static void poly_tomont(sign16 *r)
{
    int i;
    const sign16 f = KY_ONE;
    for(i=0;i<KY_DEGREE;i++)
        r[i] = montgomery_reduce((sign32)r[i]*f);
}

/* End of public domain reference code use */

// copy polynomial
static void poly_copy(sign16 *p1, sign16 *p2)
{
    int i;
    for (i = 0; i < KY_DEGREE; i++)
        p1[i] = p2[i];
}

// zero polynomial
static void poly_zero(sign16 *p1)
{
    int i;
    for (i = 0; i < KY_DEGREE; i++)
        p1[i] = 0;
}

// add polynomials
static void poly_add(sign16 *p1, sign16 *p2, sign16 *p3)
{
    int i;
    for (i = 0; i < KY_DEGREE; i++)
        p1[i] = (p2[i] + p3[i]);
}

// subtract polynomials
static void poly_sub(sign16 *p1, sign16 *p2, sign16 *p3)
{
    int i;
    for (i = 0; i < KY_DEGREE; i++)
        p1[i] = (p2[i] - p3[i]);
}

// Generate A[i][j] from rho
static void ExpandAij(byte rho[32],sign16 Aij[],int i,int j)
{
    int m;
    sha3 sh;
    SHA3_init(&sh, SHAKE128);
    byte buff[640];  // should be plenty (?)
    for (m=0;m<32;m++)
        SHA3_process(&sh,rho[m]);
    SHA3_process(&sh,j&0xff);
    SHA3_process(&sh,i&0xff);
    SHA3_shake(&sh,(char *)buff,640);
    i=j=0;
    while (j<KY_DEGREE)
    {
        int d1=buff[i]+256*(buff[i+1]&0x0F);
        int d2=buff[i+1]/16+16*buff[i+2];
        if (d1<KY_PRIME)
            Aij[j++]=d1;
        if (d2<KY_PRIME && j<KY_DEGREE)
            Aij[j++]=d2;
        i+=3;
    }
}

// get n-th bit from byte array
static int getbit(byte b[],int n)
{
    int wd=n/8;
    int bt=n%8;
    return (b[wd]>>bt)&1;
}

// centered binomial distribution
static void CBD(byte bts[],int eta,sign16 f[KY_DEGREE])
{
    int a,b;
    for (int i=0;i<KY_DEGREE;i++)
    {
        a=b=0;
        for (int j=0;j<eta;j++)
        {
            a+=getbit(bts,2*i*eta+j);
            b+=getbit(bts,2*i*eta+eta+j);
        }
        f[i]=a-b; 
    }
}

// extract ab bits into word from dense byte stream
static sign16 nextword(int ab,byte t[],int &ptr, int &bts)
{
    sign16 r=t[ptr]>>bts;
    sign16 mask=(1<<ab)-1;
    sign16 w;
    int i=0;
    int gotbits=8-bts; // bits left in current byte
    while (gotbits<ab)
    {
        i++;
        w=(sign16)t[ptr+i];
        r|=w<<gotbits;
        gotbits+=8;
    }
    bts+=ab;
    while (bts>=8)
    {
        bts-=8;
        ptr++;
    }
    w=r&mask;
    return w;  
}

// array t has ab active bits per word
// extract bytes from array of words
// if max!=0 then -max<=t[i]<=+max
static byte nextbyte16(int ab,sign16 t[],int &ptr, int &bts)
{
    sign16 r,w;
    int left=ab-bts; // number of bits left in this word
    int i=0;

    w=t[ptr]; w+=(w>>15)&KY_PRIME;
    r=w>>bts;
    while (left<8)
    {
        i++;
        w=t[ptr+i]; w+=(w>>15)&KY_PRIME;
        r|=w<<left;
        left+=ab;
    }

    bts+=8;
    while (bts>=ab)
    {
        bts-=ab;
        ptr++;
    }
    return (byte)r&0xff;        
}

// encode polynomial vector of length len with coefficients of length L, into packed bytes
static void encode(sign16 t[],int len,int L,byte pack[])
{
    int ptr,bts,n;
    ptr=bts=0;
    for (n=0;n<len*(KY_DEGREE*L)/8;n++ ) {
        pack[n]=nextbyte16(L,t,ptr,bts);
    }
}

static byte chk_encode(sign16 t[],int len,int L,byte pack[])
{
    int ptr,bts,n;
    byte m,diff=0;
    ptr=bts=0;
    for (n=0;n<len*(KY_DEGREE*L)/8;n++ ) {
        m=nextbyte16(L,t,ptr,bts);
        diff|=(m^pack[n]);
    }    
    return diff;
}

// decode packed bytes into polynomial vector of length len, with coefficients of length L
static void decode(byte pack[],int L,sign16 t[],int len)
{
    int ptr,bts,i;
    ptr=bts=0;
    for (i=0;i<len*KY_DEGREE;i++ )
        t[i]=nextword(L,pack,ptr,bts);
}

// Bernsteins safe division by 0xD01
static int32_t safediv(int32_t x)
{
    int32_t qpart,q=0;    

    qpart=(int32_t)(((int64_t)x*645083)>>31);
    x-=qpart*0xD01; q += qpart;

    qpart=(int32_t)(((int64_t)x*645083)>>31)+1;
    x-=qpart*0xD01; q += qpart+(x>>31);

    return q;
}

// compress polynomial coefficents in place, for polynomial vector of length len
static void compress(sign16 t[],int len,int d)
{
    sign32 twod=(1<<d);
    for (int i=0;i<len*KY_DEGREE;i++)
    {
        t[i]+=(t[i]>>15)&KY_PRIME;
        t[i]= (sign16)(safediv(twod*t[i]+KY_PRIME/2)&(twod-1));
    }
}

// decompress polynomial coefficents in place, for polynomial vector of length len
static void decompress(sign16 t[],int len,int d)
{
    int twod1=(1<<(d-1));
    for (int i=0;i<len*KY_DEGREE;i++)
        t[i]=(KY_PRIME*t[i]+twod1)>>d;
}

// input entropy, output key pair
static void KYBER_CPA_keypair(const int *params,byte *tau,octet *sk,octet *pk)
{
    int i,j,k,row;
    sha3 sh;
    byte rho[32];
    byte sigma[33];
    byte buff[256];

    int ck=params[0];
    sign16 r[KY_DEGREE];
    sign16 w[KY_DEGREE];
    sign16 Aij[KY_DEGREE]; 
#ifdef USE_VLAS
    sign16 s[ck*KY_DEGREE];
    sign16 e[ck*KY_DEGREE];
    sign16 p[ck*KY_DEGREE];
#else
    sign16 s[KY_MAXK*KY_DEGREE];
    sign16 e[KY_MAXK*KY_DEGREE];
    sign16 p[KY_MAXK*KY_DEGREE];
#endif

    int eta1=params[1];
    int public_key_size=32+ck*(KY_DEGREE*3)/2;
    int secret_cpa_key_size=ck*(KY_DEGREE*3)/2;

    SHA3_init(&sh,SHA3_HASH512);
   
    for (i=0;i<32;i++)
        SHA3_process(&sh,tau[i]); 
    SHA3_hash(&sh,(char *)buff);
    for (i=0;i<32;i++)
    {
        rho[i]=buff[i];
        sigma[i]=buff[i+32];
    }
    sigma[32]=0;   // N

// create s
    for (i=0;i<ck;i++)
    {
        SHA3_init(&sh,SHAKE256);
        for (j=0;j<33;j++)
            SHA3_process(&sh,sigma[j]); 
        SHA3_shake(&sh,(char *)buff,64*eta1);
        CBD(buff,eta1,&s[i*KY_DEGREE]);
        sigma[32]+=1;
    }

// create e
    for (i=0;i<ck;i++)
    {
        SHA3_init(&sh,SHAKE256);
        for (j=0;j<33;j++)
            SHA3_process(&sh,sigma[j]); 
        SHA3_shake(&sh,(char *)buff,64*eta1);
        CBD(buff,eta1,&e[i*KY_DEGREE]);
        sigma[32]+=1;
    }

    for (k=0;k<ck;k++)
    {
        row=KY_DEGREE*k;
        poly_ntt(&s[row]);
        poly_ntt(&e[row]);
    }

    for (i=0;i<ck;i++)
    {
        row=KY_DEGREE*i;
        ExpandAij(rho,Aij,i,0);
        poly_mul(r,Aij,s);
        for (j=1;j<ck;j++)
        {
            ExpandAij(rho,Aij,i,j);
            poly_mul(w,&s[j*KY_DEGREE],Aij);
            poly_add(r,r,w);
        }
        poly_reduce(r);
        poly_tomont(r);
        poly_add(&p[row],r,&e[row]);
        poly_reduce(&p[row]);
    }

    encode(s,ck,12,(byte *)sk->val);
    sk->len=secret_cpa_key_size;
    encode(p,ck,12,(byte *)pk->val);
    pk->len=public_key_size;
    for (i=0;i<32;i++)
        pk->val[public_key_size-32+i]=rho[i];
}

// input 64 random bytes, output secret and public keys
static void KYBER_CCA_keypair(const int *params,byte *randbytes64,octet *sk,octet *pk)
{
    int i;
    sha3 sh;
    byte h[32];

    KYBER_CPA_keypair(params,randbytes64,sk,pk);
    OCT_joctet(sk,pk);

    SHA3_init(&sh,SHA3_HASH256);
    for (i=0;i<pk->len;i++)
        SHA3_process(&sh,(byte)pk->val[i]);
    SHA3_hash(&sh,(char *)h);
    OCT_jbytes(sk,(char *)h,32);
    OCT_jbytes(sk,(char *)&randbytes64[32],32);
}

static void KYBER_CPA_base_encrypt(const int *params,byte *coins,octet *pk,octet *ss,sign16 *u, sign16* v)
{
    int i,row,j,len;
    sha3 sh;
    byte sigma[33];
    byte buff[256];
    byte rho[32];

    int ck=params[0];
    sign16 r[KY_DEGREE];
    sign16 w[KY_DEGREE];
    sign16 Aij[KY_DEGREE]; 
#ifdef USE_VLAS
    sign16 q[ck*KY_DEGREE];
    sign16 p[ck*KY_DEGREE];
#else
    sign16 q[KY_MAXK*KY_DEGREE];
    sign16 p[KY_MAXK*KY_DEGREE];
#endif

    int eta1=params[1];
    int eta2=params[2];
    int du=params[3];
    int dv=params[4];
    int public_key_size=32+ck*(KY_DEGREE*3)/2;

    for (i=0;i<32;i++)
        sigma[i]=coins[i];//i+6; //RAND_byte(RNG);
    sigma[32]=0;

    for (i=0;i<32;i++)
        rho[i]=pk->val[pk->len-32+i];

// create q
    for (i=0;i<ck;i++)
    {
        SHA3_init(&sh,SHAKE256);
        for (j=0;j<33;j++)
            SHA3_process(&sh,sigma[j]); 
        SHA3_shake(&sh,(char *)buff,64*eta1);
        CBD(buff,eta1,&q[i*KY_DEGREE]);
        sigma[32]+=1;
    }

// create e1
    for (i=0;i<ck;i++)
    {
        SHA3_init(&sh,SHAKE256);
        for (j=0;j<33;j++)
            SHA3_process(&sh,sigma[j]); 
        SHA3_shake(&sh,(char *)buff,64*eta2);
        CBD(buff,eta1,&u[i*KY_DEGREE]);          // e1
        sigma[32]+=1;
    }

    for (i=0;i<ck;i++)
    {
        row=KY_DEGREE*i;
        poly_ntt(&q[row]);
    }

    for (i=0;i<ck;i++)
    {
        row=KY_DEGREE*i;
        ExpandAij(rho,Aij,0,i);
        poly_mul(r,Aij,q);
        for (j=1;j<ck;j++)
        {
            ExpandAij(rho,Aij,j,i);
            poly_mul(w,&q[j*KY_DEGREE],Aij);
            poly_add(r,r,w);
        }
        poly_reduce(r);
        poly_invntt(r);
        poly_add(&u[row],&u[row],r);
        poly_reduce(&u[row]);
    }

    decode((byte *)pk->val,12,p,ck);

    poly_mul(v,p,q);
    for (i=1;i<ck;i++)
    {
        row=KY_DEGREE*i;
        poly_mul(r,&p[row],&q[row]);
        poly_add(v,v,r);
    }
    poly_invntt(v);

// create e2
    SHA3_init(&sh,SHAKE256);
    for (j=0;j<33;j++)
        SHA3_process(&sh,sigma[j]); 
    SHA3_shake(&sh,(char *)buff,64*eta2);
    CBD(buff,eta1,w);  // e2

    poly_add(v,v,w);
    
    decode((byte *)ss->val,1,r,1);
    decompress(r,1,1);
   
    poly_add(v,v,r);
    poly_reduce(v);

    compress(u,ck,du);
    compress(v,1,dv);
}

// Given input of entropy, public key and shared secret is an input, outputs ciphertext
static void KYBER_CPA_encrypt(const int *params,byte *coins,octet *pk,octet *ss,octet *ct)
{
    int ck=params[0];
    sign16 v[KY_DEGREE]; 
#ifdef USE_VLAS
    sign16 u[ck*KY_DEGREE];
#else
    sign16 u[KY_MAXK*KY_DEGREE];
#endif

    int du=params[3];
    int dv=params[4];
    int ciphertext_size=(du*ck+dv)*KY_DEGREE/8;
    KYBER_CPA_base_encrypt(params,coins,pk,ss,u,v);
    encode(u,ck,du,(byte *)ct->val);
    encode(v,1,dv,(byte *)&ct->val[ciphertext_size-(dv*KY_DEGREE/8)]);
    ct->len=ciphertext_size;
}

// Re-encrypt and check that ct is OK (if so return is zero)
static byte KYBER_CPA_check_encrypt(const int *params,byte *coins,octet *pk,octet *ss,octet *ct)
{
    int ck=params[0];
    sign16 v[KY_DEGREE]; 
#ifdef USE_VLAS
    sign16 u[ck*KY_DEGREE];
#else
    sign16 u[KY_MAXK*KY_DEGREE];
#endif  
    int du=params[3];
    int dv=params[4];
    int ciphertext_size=(du*ck+dv)*KY_DEGREE/8;
    byte d1,d2;
    KYBER_CPA_base_encrypt(params,coins,pk,ss,u,v);
    d1=chk_encode(u,ck,du,(byte *)ct->val);
    d2=chk_encode(v,1,dv,(byte *)&ct->val[ciphertext_size-(dv*KY_DEGREE/8)]);
    if ((d1|d2)==0)
        return 0;
    else
        return 0xff;
}

// Given entropy and public key, outputs 32-byte shared secret and ciphertext
static void KYBER_CCA_encrypt(const int *params,byte *randbytes32,octet *pk,octet *ss,octet *ct)
{
    int i;
    sha3 sh;
    byte h[32],hm[32],g[64],coins[32];
    octet HM={32,sizeof(hm),(char *)hm};
    int ck=params[0];
    int du=params[3];
    int dv=params[4];
    int shared_secret_size=params[5];

    SHA3_init(&sh,SHA3_HASH256);               // H(m)
    for (i=0;i<32;i++)
        SHA3_process(&sh,randbytes32[i]);
    SHA3_hash(&sh,(char *)hm);

    SHA3_init(&sh,SHA3_HASH256);               // H(pk)
    for (i=0;i<pk->len;i++)
        SHA3_process(&sh,(byte)pk->val[i]);
    SHA3_hash(&sh,(char *)h);

    SHA3_init(&sh,SHA3_HASH512);               // Kb,r = G(H(m)|H(pk)
    for (i=0;i<32;i++)
        SHA3_process(&sh,hm[i]);
    for (i=0;i<32;i++)
        SHA3_process(&sh,h[i]);
    SHA3_hash(&sh,(char *)g);

    for (i=0;i<32;i++)
        coins[i]=g[i+32];
    KYBER_CPA_encrypt(params,coins,pk,&HM,ct);
    
    SHA3_init(&sh,SHA3_HASH256);              // H(ct)
    for (i=0;i<ct->len;i++)
        SHA3_process(&sh,(byte)ct->val[i]);
    SHA3_hash(&sh,(char *)h);

    SHA3_init(&sh,SHAKE256);                  // K=KDF(Kb|H(ct))
    for (i=0;i<32;i++)
        SHA3_process(&sh,g[i]);
    for (i=0;i<32;i++)
        SHA3_process(&sh,h[i]);

    SHA3_shake(&sh,ss->val,shared_secret_size); // could be any length?
    ss->len=shared_secret_size;
}

// Input secret key and ciphertext, outputs shared 32-byte secret
static void KYBER_CPA_decrypt(const int *params,octet *sk,octet *ct,octet *ss)
{
    int i,j,row;
    int ck=params[0];
    sign16 w[KY_DEGREE];
    sign16 v[KY_DEGREE];
    sign16 r[KY_DEGREE];
#ifdef USE_VLAS
    sign16 u[ck*KY_DEGREE];
    sign16 s[ck*KY_DEGREE];
#else
    sign16 u[KY_MAXK*KY_DEGREE];
    sign16 s[KY_MAXK*KY_DEGREE];
#endif

    int du=params[3];
    int dv=params[4];
    int shared_secret_size=params[5];

    decode((byte *)ct->val,du,u,ck);
    decode((byte *)&ct->val[du*ck*KY_DEGREE/8],dv,v,1);
    decompress(u,ck,du);
    decompress(v,1,dv);
    decode((byte *)sk->val,12,s,ck);

    poly_ntt(u);
    poly_mul(w,u,s);
    for (i=1;i<ck;i++)
    {
        row=KY_DEGREE*i;
        poly_ntt(&u[row]);
        poly_mul(r,&u[row],&s[row]);
        poly_add(w,w,r);
    }
    poly_reduce(w);
    poly_invntt(w);
    poly_sub(v,v,w);
    compress(v,1,1);
    encode(v,1,1,(byte *)ss->val); 
    ss->len=shared_secret_size;
}

static void KYBER_CCA_decrypt(const int *params,octet *sk,octet *ct,octet *ss)
{ 
    int i,olen,same;
    sha3 sh;
    byte h[32],z[32],m[32],coins[32],g[64],mask;
    octet M={32,sizeof(m),(char *)m};
    int ck=params[0];
    int du=params[3];
    int dv=params[4];
    int secret_cpa_key_size=ck*(KY_DEGREE*3)/2;
    int public_key_size=32+ck*(KY_DEGREE*3)/2;
    int shared_secret_size=params[5];
    octet PK={public_key_size,public_key_size,&sk->val[secret_cpa_key_size]}; // public key is here

    olen=sk->len;
    sk->len=secret_cpa_key_size;  // chop off CPA secret
    for (i=0;i<32;i++)
        h[i]=sk->val[secret_cpa_key_size+public_key_size+i];
    for (i=0;i<32;i++)
        z[i]=sk->val[secret_cpa_key_size+public_key_size+32+i];

    KYBER_CPA_decrypt(params,sk,ct,&M);

    SHA3_init(&sh,SHA3_HASH512);               // Kb,r = G(H(m)|H(pk)
    for (i=0;i<32;i++)
        SHA3_process(&sh,m[i]);
    for (i=0;i<32;i++)
        SHA3_process(&sh,h[i]);
    SHA3_hash(&sh,(char *)g);

    for (i=0;i<32;i++)
        coins[i]=g[i+32];
    mask=KYBER_CPA_check_encrypt(params,coins,&PK,&M,ct);       // encrypt again with public key - FO transform CPA->CCA 

    for (i=0;i<32;i++)
        g[i]^=(g[i]^z[i])&mask;               // substitute z for Kb on failure

    SHA3_init(&sh,SHA3_HASH256);              // H(ct)
    for (i=0;i<ct->len;i++)
        SHA3_process(&sh,(byte)ct->val[i]);
    SHA3_hash(&sh,(char *)h);

    SHA3_init(&sh,SHAKE256);                  // K=KDF(Kb|H(ct))
    for (i=0;i<32;i++)
        SHA3_process(&sh,g[i]);
    for (i=0;i<32;i++)
        SHA3_process(&sh,h[i]);
    
    SHA3_shake(&sh,ss->val,shared_secret_size); // could be any length?
    ss->len=shared_secret_size;
    sk->len=olen; // restore length
}

// ********************* Kyber API ******************************

void core::KYBER512_keypair(byte *r64,octet *SK,octet *PK)
{
    KYBER_CCA_keypair(PARAMS_512,r64,SK,PK);
}

void core::KYBER768_keypair(byte *r64,octet *SK,octet *PK)
{
    KYBER_CCA_keypair(PARAMS_768,r64,SK,PK);
}

void core::KYBER1024_keypair(byte *r64,octet *SK,octet *PK)
{
    KYBER_CCA_keypair(PARAMS_1024,r64,SK,PK);
}

void core::KYBER512_encrypt(byte *r32,octet *PK,octet *SS,octet *CT)
{
    KYBER_CCA_encrypt(PARAMS_512,r32,PK,SS,CT);
}

void core::KYBER768_encrypt(byte *r32,octet *PK,octet *SS,octet *CT)
{
    KYBER_CCA_encrypt(PARAMS_768,r32,PK,SS,CT);
}

void core::KYBER1024_encrypt(byte *r32,octet *PK,octet *SS,octet *CT)
{
    KYBER_CCA_encrypt(PARAMS_1024,r32,PK,SS,CT);
}

void core::KYBER512_decrypt(octet *SK,octet *CT,octet *SS)
{
    KYBER_CCA_decrypt(PARAMS_512,SK,CT,SS);
}

void core::KYBER768_decrypt(octet *SK,octet *CT,octet *SS)
{
    KYBER_CCA_decrypt(PARAMS_768,SK,CT,SS);
}

void core::KYBER1024_decrypt(octet *SK,octet *CT,octet *SS)
{
    KYBER_CCA_decrypt(PARAMS_1024,SK,CT,SS);
}
