/*
* JS update 18.04.2024
* Pridana instrukcia _rdtsc() na meranie casu pri overovani certifikatov
*/

/*
* JS update 14.04.2024
* Pridana podpora pre algoritmy ML-KEM a ML-DSA
* Testovane s novou verziou liboqs 0.10.0
*/

/*
* JS update 29.02.2024
* Pridanie kniznice liboqs, nahradenie funkcii z miracl core
* Pouzitie generatoru (pseudo)nahodnych cisel z kniznice liboqs
* Uprava funkcii, ktore priamo vyuzivaju pole s vygenerovanymi cislami
* Definovanie novych PQ KEM algoritmov do zoznamu
* Pridane nove PQ algoritmy pre certifikaty a podpis
* Pridane funkcie pre generovanie sukromneho a verejneho kluca pre KEM protokoly
* Pridane funkcie na decapsulaciu pre KEM protokoly
* Pridane funkcii na overenie a podpis novymi PQ algoritmami
*/

// JS update
// Cryptographic Security Abstraction Layer API - this version uses MIRACL core & liboqs functions
//

#include "tls_sal.h"

// Pull in MIRACL core code

#include "core.h"
#include "ecdh_NIST256.h"  
#include "ecdh_NIST384.h"
#include "ecdh_C25519.h"
#include "rsa_RSA2048.h"
#include "rsa_RSA4096.h"
#include "eddsa_Ed25519.h"
#include "eddsa_Ed448.h"
#include <intrin.h>
#pragma intrinsic(__rdtsc)
#if CRYPTO_SETTING>=POST_QUANTUM
// JS post-kvantove algoritmy pridane z kniznice liboqs
#include <oqs/oqs.h>
#include <oqs/oqsconfig.h>

#endif

using namespace core;

csprng RNG;    // Global miracl core Crypto Strong RNG - could be a hardware source

// JS definovanie pola pre vygenerovanie 64 nahodnych cisel pouzitych ako seed pre RNG
uint8_t RNGseed[64];

// JS pridanie liboqs do vypisu
char* SAL_name()
{
    return (char *)"MIRACL Core + liboqs"; 
}

// JS pole naplnene nahodne vygenerovanymi cislami a pouzite ako seed pre RNG generator
bool SAL_initLib()
{ // Initialise library
#if CRYPTO_SETTING>=POST_QUANTUM
    OQS_randombytes(RNGseed, 64);

#else
    char raw[64];
    for (int i = 0; i < 64; i++) raw[i] = i;  // *** should be from output of true random number generator ****
#endif
    RAND_seed(&RNG, 64, (char*) RNGseed);    
    return true;
}

void SAL_endLib()
{
}

int SAL_ciphers(int *ciphers)
{
    int n=2;
    ciphers[0]=TLS_AES_128_GCM_SHA256;
    ciphers[1]=TLS_AES_256_GCM_SHA384;
    return n;
}

// IMPORTANT - Favourite group (as used in client Hello) must be placed first in list
int SAL_groups(int *groups)
{
    int n=0;

#if CRYPTO_SETTING==TINY_ECC || CRYPTO_SETTING==EDDSA
    n=3;
    groups[0]=X25519;
    groups[1]=SECP256R1;
    groups[2]=SECP384R1;
#endif

#if CRYPTO_SETTING==TYPICAL
    n=3;
    groups[0]=X25519;
    groups[1]=SECP256R1;
    groups[2]=SECP384R1;
#endif

// JS doplnene PQ KEM algoritmy
// BIKE nefunguje na Windowse, preto je jeho pridanie do zoznamu podmienene linuxovym prostredim
#if CRYPTO_SETTING==POST_QUANTUM
    n=0;
   	groups[n]=KYBER512; n++;
	groups[n]=KYBER768; n++;
	groups[n]=KYBER1024; n++;
    groups[n]=MLKEM512; n++;
    groups[n]=MLKEM768; n++;
    groups[n]=MLKEM1024; n++;
	groups[n]=HQC128; n++;
	groups[n]=HQC192; n++;
	groups[n]=HQC256; n++;
	groups[n]=FRODO640AES; n++;
	groups[n]=FRODO640SHAKE; n++;
	groups[n]=FRODO976AES; n++;
	groups[n]=FRODO976SHAKE; n++;
	groups[n]=FRODO1344AES; n++;
	groups[n]=FRODO1344SHAKE; n++;
#ifdef __linux__
	groups[n]=BIKEL1; n++;
	groups[n]=BIKEL3; n++;
	groups[n]=BIKEL5; n++;
#endif

#endif

    return n;
}

int SAL_sigs(int *sigAlgs)
{
    int n=4;
    sigAlgs[0]=ECDSA_SECP256R1_SHA256;
    sigAlgs[1]=ECDSA_SECP384R1_SHA384;
    sigAlgs[2]=ED25519;
    sigAlgs[3]=ED448;
#if CRYPTO_SETTING>TINY_ECC
    sigAlgs[n]=RSA_PSS_RSAE_SHA256; n++;
#endif

// JS doplnene PQ algoritmy
#if CRYPTO_SETTING>=POST_QUANTUM
	sigAlgs[n]=DILITHIUM2; n++;
    sigAlgs[n]=DILITHIUM3; n++;
	sigAlgs[n]=DILITHIUM5; n++;
    sigAlgs[n]=MLDSA44; n++;
    sigAlgs[n]=MLDSA65; n++;
    sigAlgs[n]=MLDSA87; n++;
    sigAlgs[n]=FALCON512; n++;
    sigAlgs[n]=FALCON1024; n++;
    sigAlgs[n]=SPHINCSSHA2128FSIMPLE; n++;
    sigAlgs[n]=SPHINCSSHA2128SSIMPLE; n++; 
    sigAlgs[n]=SPHINCSSHAKE128FSIMPLE; n++;
    //sigAlgs[n]=SPHINCSSHA2192FSIMPLE; n++;
#endif
    return n;
}

int SAL_sigCerts(int *sigAlgsCert)
{
    int n=4;
    sigAlgsCert[0]=ECDSA_SECP256R1_SHA256;
    sigAlgsCert[1]=ECDSA_SECP384R1_SHA384;
    sigAlgsCert[2]=ED25519;
    sigAlgsCert[3]=ED448;
#if CRYPTO_SETTING>TINY_ECC
    sigAlgsCert[n]=RSA_PKCS1_SHA256; n++;
    sigAlgsCert[n]=RSA_PKCS1_SHA384; n++;
    sigAlgsCert[n]=RSA_PKCS1_SHA512; n++;
#endif

// JS doplnene PQ algoritmy
#if CRYPTO_SETTING>=POST_QUANTUM
    sigAlgsCert[n]=DILITHIUM2; n++;
    sigAlgsCert[n]=DILITHIUM3; n++;
    sigAlgsCert[n]=DILITHIUM5; n++;
    sigAlgsCert[n]=MLDSA44; n++;
    sigAlgsCert[n]=MLDSA65; n++;
    sigAlgsCert[n]=MLDSA87; n++;
    sigAlgsCert[n]=FALCON512; n++;
    sigAlgsCert[n]=FALCON1024; n++;
    sigAlgsCert[n]=SPHINCSSHA2128FSIMPLE; n++;
    sigAlgsCert[n]=SPHINCSSHA2128SSIMPLE; n++;
    sigAlgsCert[n]=SPHINCSSHAKE128FSIMPLE; n++;
    //sigAlgsCert[n]=SPHINCSSHA2192FSIMPLE; n++;
#endif
    return n;
}

// return hashtype from cipher_suite
int SAL_hashType(int cipher_suite)
{
    int htype=0;  
    if (cipher_suite==TLS_AES_128_GCM_SHA256) htype=TLS_SHA256_T;
    if (cipher_suite==TLS_AES_256_GCM_SHA384) htype=TLS_SHA384_T;
    if (cipher_suite==TLS_CHACHA20_POLY1305_SHA256) htype=TLS_SHA256_T;
    return htype;
}

/*
// return hashtype from signature algorithm
int SAL_hashTypeSig(int sigAlg)
{
    int htype=0;  
    if (sigAlg==ECDSA_SECP256R1_SHA256) htype=TLS_SHA256_T;
    if (sigAlg==ECDSA_SECP384R1_SHA384) htype=TLS_SHA384_T;
    if (sigAlg==RSA_PSS_RSAE_SHA256) htype=TLS_SHA256_T;
    if (sigAlg==RSA_PSS_RSAE_SHA384) htype=TLS_SHA384_T;
    if (sigAlg==RSA_PSS_RSAE_SHA512) htype=TLS_SHA512_T;
    if (sigAlg==RSA_PKCS1_SHA256) htype=TLS_SHA256_T;
    if (sigAlg==RSA_PKCS1_SHA384) htype=TLS_SHA384_T;
    if (sigAlg==RSA_PKCS1_SHA512) htype=TLS_SHA512_T;
    return htype;
}
*/ 
// return hash length from hash type
int SAL_hashLen(int hash_type)
{
    int hlen=0;
    if (hash_type==TLS_SHA256_T) hlen=32;
    if (hash_type==TLS_SHA384_T) hlen=48;
    if (hash_type==TLS_SHA512_T) hlen=64;
    return hlen;
}

int SAL_aeadKeylen(int cipher_suite)
{
    int klen=0;
    if (cipher_suite==TLS_AES_128_GCM_SHA256) klen=16;
    if (cipher_suite==TLS_AES_256_GCM_SHA384) klen=32;
    if (cipher_suite==TLS_CHACHA20_POLY1305_SHA256) klen=32;
    return klen;
}

int SAL_aeadTaglen(int cipher_suite)
{
    int tlen=0;
    if (cipher_suite==TLS_AES_128_GCM_SHA256) tlen=16;
    if (cipher_suite==TLS_AES_256_GCM_SHA384) tlen=16;
    if (cipher_suite==TLS_CHACHA20_POLY1305_SHA256) tlen=16;
    return tlen;
}

// convert TLS octad to MIRACL core octet
static octet octad_to_octet(octad *x)
{
    octet y;
    if (x!=NULL) {
        y.len=x->len;
        y.max=x->max;
        y.val=x->val;
    } else {
        y.len=y.max=0;
        y.val=NULL;
    }
    return y;
}

// Return a random byte
int SAL_randomByte()
{
    return RAND_byte(&RNG);
}

// Fill an octad with random values
void SAL_randomOctad(int len,octad *R)
{
    for (int i=0;i<len;i++)
        R->val[i]=SAL_randomByte();
    R->len=len;
}

// HKDF - Extract secret from raw input
void SAL_hkdfExtract(int htype,octad *PRK,octad *SALT,octad *IKM)
{
    int hlen=SAL_hashLen(htype);
    octet MC_PRK=octad_to_octet(PRK);   // Make it MIRACL core compatible
    octet MC_SALT=octad_to_octet(SALT);
    octet MC_IKM=octad_to_octet(IKM);

    HKDF_Extract(MC_SHA2,hlen,&MC_PRK,&MC_SALT,&MC_IKM);

    IKM->len=MC_IKM.len;              // restore length
    SALT->len=MC_SALT.len;
    PRK->len=MC_PRK.len;
}

void SAL_hkdfExpand(int htype, int olen, octad *OKM,octad *PRK, octad *INFO)
{
    int hlen=SAL_hashLen(htype);
    octet MC_OKM=octad_to_octet(OKM); 
    octet MC_INFO=octad_to_octet(INFO);
    octet MC_PRK=octad_to_octet(PRK);

    HKDF_Expand(MC_SHA2,hlen,&MC_OKM,olen,&MC_PRK,&MC_INFO);

    OKM->len=MC_OKM.len;
    INFO->len=MC_INFO.len;
    PRK->len=MC_PRK.len;
}

// HMAC
void SAL_hmac(int htype,octad *T,octad *K,octad *M)
{
    int hlen=SAL_hashLen(htype);
    octet MC_T=octad_to_octet(T);
    octet MC_K=octad_to_octet(K);
    octet MC_M=octad_to_octet(M);

    HMAC(MC_SHA2,hlen,&MC_T,hlen,&MC_K,&MC_M);

    T->len=MC_T.len;
    K->len=MC_K.len;
    M->len=MC_M.len;
}

// HASH of NULL
void SAL_hashNull(int htype,octad *H)
{
    if (htype==TLS_SHA256_T)
    {
        core::hash256 sh;
        HASH256_init(&sh);
        HASH256_hash(&sh,H->val);
    } 
    if (htype==TLS_SHA384_T)
    {
        core::hash384 sh;
        HASH384_init(&sh);
        HASH384_hash(&sh,H->val);
    }     
    if (htype==TLS_SHA512_T)
    {
        core::hash512 sh;
        HASH512_init(&sh);
        HASH512_hash(&sh,H->val);

    }     
    H->len=SAL_hashLen(htype);
    return;    
}

// Unified hashing. Hash type type indicate by htype.
void SAL_hashInit(int htype,unihash *h)
{
    if (htype==TLS_SHA256_T) 
        HASH256_init((core::hash256*)&(h->state));
    if (htype==TLS_SHA384_T)
        HASH384_init((core::hash512*)&(h->state));
    if (htype==TLS_SHA512_T)
        HASH512_init((core::hash512*)&(h->state));
    h->htype=htype;
}

// Process a byte array
void SAL_hashProcessArray(unihash *h,char *b,int len)
{
    int i;
    if (h->htype==TLS_SHA256_T)
    {
        for (i=0;i<len;i++)
            HASH256_process((core::hash256*)&(h->state),b[i]);   
    }   
    if (h->htype==TLS_SHA384_T)
    {
        for (i=0;i<len;i++)
            HASH384_process((core::hash512*)&(h->state),b[i]);   
    }  
    if (h->htype==TLS_SHA512_T)
    {
        for (i=0;i<len;i++)
            HASH512_process((core::hash512*)&(h->state),b[i]);   
    }     
}

// output digest
int SAL_hashOutput(unihash *h,char *d)
{
    int hlen=SAL_hashLen(h->htype);
    if (h->htype==TLS_SHA256_T)
        HASH256_continuing_hash((core::hash256*)&(h->state),d);
    if (h->htype==TLS_SHA384_T)
        HASH384_continuing_hash((core::hash384*)&(h->state),d);
    if (h->htype==TLS_SHA512_T)
        HASH512_continuing_hash((core::hash512*)&(h->state),d);
    return hlen;
}

void SAL_aeadEncrypt(crypto *send,int hdrlen,char *hdr,int ptlen,char *pt,octad *TAG)
{ // AEAD encryption
  // its AES-GCM
    gcm g;
    GCM_init(&g,send->K.len,send->K.val,12,send->IV.val);  // Encrypt with Key and IV
    GCM_add_header(&g,hdr,hdrlen);
    GCM_add_plain(&g,pt,pt,ptlen);
//create and append TA
    GCM_finish(&g,TAG->val); 
    TAG->len=16;
}

bool SAL_aeadDecrypt(crypto *recv,int hdrlen,char *hdr,int ctlen,char *ct,octad *TAG)
{ // AEAD decryption
  // its AES-GCM
    char ctag[TLS_MAX_TAG_SIZE];   // calculated TAG
    octad CTAG={0,sizeof(ctag),ctag};
    gcm g;
    GCM_init(&g,recv->K.len,recv->K.val,12,recv->IV.val);  // Decrypt with Key and IV
    GCM_add_header(&g,hdr,hdrlen);
    GCM_add_cipher(&g,ct,ct,ctlen);
//create and append TA
    GCM_finish(&g,CTAG.val); CTAG.len=16;
    if (!OCT_compare(TAG,&CTAG))
        return false;
    return true;
}

// generate a public/private key pair in an approved group for a key exchange
void SAL_generateKeyPair(int group,octad *SK,octad *PK)
{
    octet MC_SK=octad_to_octet(SK);
    octet MC_PK=octad_to_octet(PK);

    if (group==X25519)
    { // Note that this program maintains the private key in big-endian format
      // But X25519 assumes private and public keys are all in little-endian form (and are transmitted/received in that form)
        SAL_randomOctad(32,SK);
        SK->val[31]&=248;  
        SK->val[0]&=127;
        SK->val[0]|=64;
        C25519::ECP_KEY_PAIR_GENERATE(NULL, &MC_SK, &MC_PK);
        OCT_reverse(&MC_PK);  // public key must be transmitted in little-endian form
    }
    if (group==SECP256R1)
    {
        SAL_randomOctad(32,SK);
        NIST256::ECP_KEY_PAIR_GENERATE(NULL, &MC_SK, &MC_PK);
    }
    if (group==SECP384R1)
    {
        SAL_randomOctad(48,SK);
        NIST384::ECP_KEY_PAIR_GENERATE(NULL, &MC_SK, &MC_PK);
    }
	SK->len=MC_SK.len;
    	PK->len=MC_PK.len;

//--------------------------------------------------------------------------
// JS doplnene PQ KEM algoritmy 
#if CRYPTO_SETTING>=POST_QUANTUM
    if (group==KYBER768){
        OQS_STATUS rc = OQS_KEM_kyber_768_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_kyber_768_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_kyber_768_length_secret_key;
            PK->len=OQS_KEM_kyber_768_length_public_key;
        }
    }

    if (group==KYBER512){
        OQS_STATUS rc = OQS_KEM_kyber_512_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_kyber_512_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_kyber_512_length_secret_key;
            PK->len=OQS_KEM_kyber_512_length_public_key;
        }
    }

    if (group==KYBER1024){
        OQS_STATUS rc = OQS_KEM_kyber_1024_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_kyber_1024_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_kyber_1024_length_secret_key;
            PK->len=OQS_KEM_kyber_1024_length_public_key;
        }
    }

    if (group==MLKEM512){
        OQS_STATUS rc = OQS_KEM_ml_kem_512_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_ml_kem_512_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_ml_kem_512_length_secret_key;
            PK->len=OQS_KEM_ml_kem_512_length_public_key;
        }
    }

    if (group==MLKEM768){
        OQS_STATUS rc = OQS_KEM_ml_kem_768_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_ml_kem_768_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_ml_kem_768_length_secret_key;
            PK->len=OQS_KEM_ml_kem_768_length_public_key;
        }
    }

    if (group==MLKEM1024){
        OQS_STATUS rc = OQS_KEM_ml_kem_1024_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_ml_kem_1024_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_ml_kem_1024_length_secret_key;
            PK->len=OQS_KEM_ml_kem_1024_length_public_key;
        }
    }

// JS Protokol BIKE nie je podporovany na Windowse, preto je definovany len pre linuxove prostredie
// Bez podmienky aplikacia na Windowse padne, pretoze liboqs na Windowse neobsahuje funkcie pre BIKE
#ifdef __linux__
    if (group==BIKEL1){
        OQS_STATUS rc = OQS_KEM_bike_l1_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_bike_l1_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_bike_l1_length_secret_key;
            PK->len=OQS_KEM_bike_l1_length_public_key;
        }
    }

    if (group==BIKEL3){
        OQS_STATUS rc = OQS_KEM_bike_l3_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_bike_l3_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_bike_l3_length_secret_key;
            PK->len=OQS_KEM_bike_l3_length_public_key;
        }
    }

    if (group==BIKEL5){
        OQS_STATUS rc = OQS_KEM_bike_l5_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_bike_l5_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_bike_l5_length_secret_key;
            PK->len=OQS_KEM_bike_l5_length_public_key;
        }
    }
#endif

    if (group==HQC128){
        OQS_STATUS rc = OQS_KEM_hqc_128_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_hqc_128_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_hqc_128_length_secret_key;
            PK->len=OQS_KEM_hqc_128_length_public_key;
        }
    }

    if (group==HQC192){
        OQS_STATUS rc = OQS_KEM_hqc_192_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_hqc_192_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_hqc_192_length_secret_key;
            PK->len=OQS_KEM_hqc_192_length_public_key;
        }
    }

   if (group==HQC256){
        OQS_STATUS rc = OQS_KEM_hqc_256_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_hqc_256_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_hqc_256_length_secret_key;
            PK->len=OQS_KEM_hqc_256_length_public_key;
        }
    }

   if (group==FRODO640AES){
        OQS_STATUS rc = OQS_KEM_frodokem_640_aes_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_frodokem_640_aes_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_frodokem_640_aes_length_secret_key;
            PK->len=OQS_KEM_frodokem_640_aes_length_public_key;
        }
    }

   if (group==FRODO976AES){
        OQS_STATUS rc = OQS_KEM_frodokem_976_aes_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_frodokem_976_aes_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_frodokem_976_aes_length_secret_key;
            PK->len=OQS_KEM_frodokem_976_aes_length_public_key;
        }
    }

   if (group==FRODO1344AES){
        OQS_STATUS rc = OQS_KEM_frodokem_1344_aes_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_frodokem_1344_aes_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_frodokem_1344_aes_length_secret_key;
            PK->len=OQS_KEM_frodokem_1344_aes_length_public_key;
        }
    }


   if (group==FRODO640SHAKE){
        OQS_STATUS rc = OQS_KEM_frodokem_640_shake_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_frodokem_640_shake_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_frodokem_640_shake_length_secret_key;
            PK->len=OQS_KEM_frodokem_640_shake_length_public_key;
        }
    }

   if (group==FRODO976SHAKE){
        OQS_STATUS rc = OQS_KEM_frodokem_976_shake_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_frodokem_976_shake_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_frodokem_976_shake_length_secret_key;
            PK->len=OQS_KEM_frodokem_976_shake_length_public_key;
        }
    }

   if (group==FRODO1344SHAKE){
        OQS_STATUS rc = OQS_KEM_frodokem_1344_shake_keypair((uint8_t*) PK->val, (uint8_t*) SK->val);
	    if (rc != OQS_SUCCESS) {
		    fprintf(stderr, "ERROR: OQS_KEM_frodokem_1344_shake_keypair failed!\n");
            SK->len=0;
            PK->len=0;
        }
        else{
            SK->len=OQS_KEM_frodokem_1344_shake_length_secret_key;
            PK->len=OQS_KEM_frodokem_1344_shake_length_public_key;		
        }
    }
//--------------------------------------------------------------------------

#endif
}

// generate shared secret SS from secret key SK and public key PK
bool SAL_generateSharedSecret(int group,octad *SK,octad *PK,octad *SS)
{
    octet MC_SK=octad_to_octet(SK);
    octet MC_PK=octad_to_octet(PK);
    octet MC_SS=octad_to_octet(SS);
    int res=0;

    if (group==X25519) {
        OCT_reverse(&MC_PK); // to big endian
        res=C25519::ECP_SVDP_DH(&MC_SK, &MC_PK, &MC_SS,0);
        OCT_reverse(&MC_PK); // back again
        OCT_reverse(&MC_SS);
    }
    if (group==SECP256R1) {
        res=NIST256::ECP_SVDP_DH(&MC_SK, &MC_PK, &MC_SS,0);
    }
    if (group==SECP384R1) {
        res=NIST384::ECP_SVDP_DH(&MC_SK, &MC_PK, &MC_SS,0);
    }
    SK->len=MC_SK.len;
    PK->len=MC_PK.len;
    SS->len=MC_SS.len;

//--------------------------------------------------------------------------
// JS doplnene PQ KEM algoritmy
// Zo strany klienta je pri KEM protokole realizovana len decapsulacia
#if CRYPTO_SETTING>=POST_QUANTUM
    if (group==KYBER768) {
        OQS_STATUS rc = OQS_KEM_kyber_768_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_kyber_768_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_kyber_768_length_secret_key;
            PK->len=OQS_KEM_kyber_768_length_ciphertext;
            SS->len=OQS_KEM_kyber_768_length_shared_secret;
        }
    }
if (group==KYBER512) {
        OQS_STATUS rc = OQS_KEM_kyber_512_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_kyber_512_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_kyber_512_length_secret_key;
            PK->len=OQS_KEM_kyber_512_length_ciphertext;
            SS->len=OQS_KEM_kyber_512_length_shared_secret;
        }
    }
if (group==KYBER1024) {
        OQS_STATUS rc = OQS_KEM_kyber_1024_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_kyber_1024_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_kyber_1024_length_secret_key;
            PK->len=OQS_KEM_kyber_1024_length_ciphertext;
            SS->len=OQS_KEM_kyber_1024_length_shared_secret;
        }
    }

if (group==MLKEM512) {
        OQS_STATUS rc = OQS_KEM_ml_kem_512_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_ml_kem_512_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_ml_kem_512_length_secret_key;
            PK->len=OQS_KEM_ml_kem_512_length_ciphertext;
            SS->len=OQS_KEM_ml_kem_512_length_shared_secret;
        }
    }

if (group==MLKEM768) {
        OQS_STATUS rc = OQS_KEM_ml_kem_768_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_ml_kem_768_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_ml_kem_768_length_secret_key;
            PK->len=OQS_KEM_ml_kem_768_length_ciphertext;
            SS->len=OQS_KEM_ml_kem_768_length_shared_secret;
        }
    }

if (group==MLKEM1024) {
        OQS_STATUS rc = OQS_KEM_ml_kem_1024_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_ml_kem_1024_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_ml_kem_1024_length_secret_key;
            PK->len=OQS_KEM_ml_kem_1024_length_ciphertext;
            SS->len=OQS_KEM_ml_kem_1024_length_shared_secret;
        }
    }

// JS Protokol BIKE nie je podporovany na Windowse, preto je definovany len pre linuxove prostredie
// Bez podmienky aplikacia na Windowse padne, pretoze liboqs na Windowse neobsahuje funkcie pre BIKE
#ifdef __linux__
if (group==BIKEL1) {
        OQS_STATUS rc = OQS_KEM_bike_l1_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_bike_l1_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_bike_l1_length_secret_key;
            PK->len=OQS_KEM_bike_l1_length_ciphertext;
            SS->len=OQS_KEM_bike_l1_length_shared_secret;
        }
    }

if (group==BIKEL3) {
        OQS_STATUS rc = OQS_KEM_bike_l3_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_bike_l3_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_bike_l3_length_secret_key;
            PK->len=OQS_KEM_bike_l3_length_ciphertext;
            SS->len=OQS_KEM_bike_l3_length_shared_secret;
        }
    }

if (group==BIKEL5) {
        OQS_STATUS rc = OQS_KEM_bike_l5_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_bike_l5_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_bike_l5_length_secret_key;
            PK->len=OQS_KEM_bike_l5_length_ciphertext;
            SS->len=OQS_KEM_bike_l5_length_shared_secret;
        }
    }
#endif

if (group==HQC128) {
        OQS_STATUS rc = OQS_KEM_hqc_128_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_hqc_128_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_hqc_128_length_secret_key;
            PK->len=OQS_KEM_hqc_128_length_ciphertext;
            SS->len=OQS_KEM_hqc_128_length_shared_secret;
        }
    }

if (group==HQC192) {
        OQS_STATUS rc = OQS_KEM_hqc_192_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_hqc_192_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_hqc_192_length_secret_key;
            PK->len=OQS_KEM_hqc_192_length_ciphertext;
            SS->len=OQS_KEM_hqc_192_length_shared_secret;
        }
    }

if (group==HQC256) {
        OQS_STATUS rc = OQS_KEM_hqc_256_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_hqc_256_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_hqc_256_length_secret_key;
            PK->len=OQS_KEM_hqc_256_length_ciphertext;
            SS->len=OQS_KEM_hqc_256_length_shared_secret;
        }
    }

if (group==FRODO640AES) {
        OQS_STATUS rc = OQS_KEM_frodokem_640_aes_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_frodokem_640_aes_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_frodokem_640_aes_length_secret_key;
            PK->len=OQS_KEM_frodokem_640_aes_length_ciphertext;
            SS->len=OQS_KEM_frodokem_640_aes_length_shared_secret;
        }
    }

if (group==FRODO976AES) {
        OQS_STATUS rc = OQS_KEM_frodokem_976_aes_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_frodokem_976_aes_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_frodokem_976_aes_length_secret_key;
            PK->len=OQS_KEM_frodokem_976_aes_length_ciphertext;
            SS->len=OQS_KEM_frodokem_976_aes_length_shared_secret;
        }
    }

if (group==FRODO1344AES) {
        OQS_STATUS rc = OQS_KEM_frodokem_1344_aes_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_frodokem_1344_aes_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_frodokem_1344_aes_length_secret_key;
            PK->len=OQS_KEM_frodokem_1344_aes_length_ciphertext;
            SS->len=OQS_KEM_frodokem_1344_aes_length_shared_secret;
        }
    }

if (group==FRODO640SHAKE) {
        OQS_STATUS rc = OQS_KEM_frodokem_640_shake_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_frodokem_640_shake_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_frodokem_640_shake_length_secret_key;
            PK->len=OQS_KEM_frodokem_640_shake_length_ciphertext;
            SS->len=OQS_KEM_frodokem_640_shake_length_shared_secret;
        }
    }

if (group==FRODO976SHAKE) {
        OQS_STATUS rc = OQS_KEM_frodokem_976_shake_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_frodokem_976_shake_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_frodokem_976_shake_length_secret_key;
            PK->len=OQS_KEM_frodokem_976_shake_length_ciphertext;
            SS->len=OQS_KEM_frodokem_976_shake_length_shared_secret;
        }
    }

if (group==FRODO1344SHAKE) {
        OQS_STATUS rc = OQS_KEM_frodokem_1344_shake_decaps( (uint8_t*) SS->val, (uint8_t*) PK->val, (uint8_t*) SK->val);
    	if (rc != OQS_SUCCESS) {
	    	fprintf(stderr, "ERROR: OQS_KEM_frodokem_1344_shake_decaps failed!\n");
            SS->len=0;
        }
        else{
            SK->len=OQS_KEM_frodokem_1344_shake_length_secret_key;
            PK->len=OQS_KEM_frodokem_1344_shake_length_ciphertext;
            SS->len=OQS_KEM_frodokem_1344_shake_length_shared_secret;
        }
    }
//--------------------------------------------------------------------------
#endif
    
// all zeros is suspect...
    char ors=0;
    for (int i=0;i<SS->len;i++) ors|=SS->val[i];
    if (ors==0 || res!=0) return false;
    return true;
}

//--------------------------------------------------------------------------
// JS funkcie pre nove PQ algoritmy
// Najprv funkcia pre overenie, potom funkcia pre podpis
#if CRYPTO_SETTING>=POST_QUANTUM
static bool DILITHIUM3_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{
    OQS_STATUS rc = OQS_SIG_dilithium_3_verify((uint8_t*) CERT->val, CERT->len, (uint8_t*) SIG->val, SIG->len, (uint8_t*) PUBKEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_dilithium_3_verify failed!\n");
        return false;
    }
    return true;  
}

static void DILITHIUM3_SIGN(octad *KEY,octad *MESS,octad *SIG)
{
    OQS_STATUS rc = OQS_SIG_dilithium_3_sign( (uint8_t*) SIG->val, (size_t*) SIG->len, (uint8_t*) MESS->val, (size_t) MESS->len, (uint8_t*) KEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_dilithium_3_sign failed!\n");
        SIG->len=0;
    }
    else{
        SIG->len=OQS_SIG_dilithium_3_length_signature;
    }    
}

static bool DILITHIUM2_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{
    OQS_STATUS rc = OQS_SIG_dilithium_2_verify((uint8_t*) CERT->val, CERT->len, (uint8_t*) SIG->val, SIG->len, (uint8_t*) PUBKEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_dilithium_2_verify failed!\n");
        return false;
    }
    return true;  
}

static void DILITHIUM2_SIGN(octad *KEY,octad *MESS,octad *SIG)
{
    OQS_STATUS rc = OQS_SIG_dilithium_2_sign( (uint8_t*) SIG->val, (size_t*) SIG->len, (uint8_t*) MESS->val, (size_t) MESS->len, (uint8_t*) KEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_dilithium_2_sign failed!\n");
        SIG->len=0;
    }
    else{
        SIG->len=OQS_SIG_dilithium_2_length_signature;
    }    
}


static bool DILITHIUM5_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{
    OQS_STATUS rc = OQS_SIG_dilithium_5_verify((uint8_t*) CERT->val, CERT->len, (uint8_t*) SIG->val, SIG->len, (uint8_t*) PUBKEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_dilithium_5_verify failed!\n");
        return false;
    }
    return true;  
}

static void DILITHIUM5_SIGN(octad *KEY,octad *MESS,octad *SIG)
{
    OQS_STATUS rc = OQS_SIG_dilithium_5_sign( (uint8_t*) SIG->val, (size_t*) SIG->len, (uint8_t*) MESS->val, (size_t) MESS->len, (uint8_t*) KEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_dilithium_5_sign failed!\n");
        SIG->len=0;
    }
    else{
        SIG->len=OQS_SIG_dilithium_5_length_signature;
    }    
}

//-----

static bool MLDSA44_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{
    OQS_STATUS rc = OQS_SIG_ml_dsa_44_ipd_verify((uint8_t*) CERT->val, CERT->len, (uint8_t*) SIG->val, SIG->len, (uint8_t*) PUBKEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_ml_dsa_44_ipd_verify failed!\n");
        return false;
    }
    return true;  
}

static void MLDSA44_SIGN(octad *KEY,octad *MESS,octad *SIG)
{
    OQS_STATUS rc = OQS_SIG_ml_dsa_44_ipd_sign( (uint8_t*) SIG->val, (size_t*) SIG->len, (uint8_t*) MESS->val, (size_t) MESS->len, (uint8_t*) KEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_ml_dsa_44_ipd_sign failed!\n");
        SIG->len=0;
    }
    else{
        SIG->len=OQS_SIG_ml_dsa_44_ipd_length_signature;
    }    
}

static bool MLDSA65_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{
    OQS_STATUS rc = OQS_SIG_ml_dsa_65_ipd_verify((uint8_t*) CERT->val, CERT->len, (uint8_t*) SIG->val, SIG->len, (uint8_t*) PUBKEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_ml_dsa_65_ipd_verify failed!\n");
        return false;
    }
    return true;  
}

static void MLDSA65_SIGN(octad *KEY,octad *MESS,octad *SIG)
{
    OQS_STATUS rc = OQS_SIG_ml_dsa_65_ipd_sign( (uint8_t*) SIG->val, (size_t*) SIG->len, (uint8_t*) MESS->val, (size_t) MESS->len, (uint8_t*) KEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_ml_dsa_65_ipd_sign failed!\n");
        SIG->len=0;
    }
    else{
        SIG->len=OQS_SIG_ml_dsa_65_ipd_length_signature;
    }    
}

static bool MLDSA87_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{
    OQS_STATUS rc = OQS_SIG_ml_dsa_87_ipd_verify((uint8_t*) CERT->val, CERT->len, (uint8_t*) SIG->val, SIG->len, (uint8_t*) PUBKEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_ml_dsa_87_ipd_verify failed!\n");
        return false;
    }
    return true;  
}

static void MLDSA87_SIGN(octad *KEY,octad *MESS,octad *SIG)
{
    OQS_STATUS rc = OQS_SIG_ml_dsa_87_ipd_sign( (uint8_t*) SIG->val, (size_t*) SIG->len, (uint8_t*) MESS->val, (size_t) MESS->len, (uint8_t*) KEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_ml_dsa_87_ipd_sign failed!\n");
        SIG->len=0;
    }
    else{
        SIG->len=OQS_SIG_ml_dsa_87_ipd_length_signature;
    }    
}


// ------
static bool FALCON512_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{
    OQS_STATUS rc = OQS_SIG_falcon_512_verify((uint8_t*) CERT->val, CERT->len, (uint8_t*) SIG->val, SIG->len, (uint8_t*) PUBKEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_falcon_512_verify failed!\n");
        return false;
    }
    return true;  
}

static void FALCON512_SIGN(octad *KEY,octad *MESS,octad *SIG)
{
    OQS_STATUS rc = OQS_SIG_falcon_512_sign( (uint8_t*) SIG->val, (size_t*) SIG->len, (uint8_t*) MESS->val, (size_t) MESS->len, (uint8_t*) KEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_falcon_512_sign failed!\n");
        SIG->len=0;
    }
    else{
        SIG->len=OQS_SIG_falcon_512_length_signature;
    }    
}

static bool FALCON1024_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{
    OQS_STATUS rc = OQS_SIG_falcon_1024_verify((uint8_t*) CERT->val, CERT->len, (uint8_t*) SIG->val, SIG->len, (uint8_t*) PUBKEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_falcon_1024_verify failed!\n");
        return false;
    }
    return true;  
}

static void FALCON1024_SIGN(octad *KEY,octad *MESS,octad *SIG)
{
    OQS_STATUS rc = OQS_SIG_falcon_1024_sign( (uint8_t*) SIG->val, (size_t*) SIG->len, (uint8_t*) MESS->val, (size_t) MESS->len, (uint8_t*) KEY->val);
	if (rc != OQS_SUCCESS) {
		fprintf(stderr, "ERROR: OQS_SIG_falcon_1024_sign failed!\n");
        SIG->len=0;
    }
    else{
        SIG->len=OQS_SIG_falcon_1024_length_signature;
    }    
}

static bool SPHINCSSHA2128FSIMPLE_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{
    OQS_STATUS rc = OQS_SIG_sphincs_sha2_128f_simple_verify((uint8_t*) CERT->val, CERT->len, (uint8_t*) SIG->val, SIG->len, (uint8_t*) PUBKEY->val);
	if (rc != OQS_SUCCESS) {
        return false;
    }
    return true;  
}

static void SPHINCSSHA2128FSIMPLE_SIGN(octad *KEY,octad *MESS,octad *SIG)
{
    OQS_STATUS rc = OQS_SIG_sphincs_sha2_128f_simple_sign( (uint8_t*) SIG->val, (size_t*) SIG->len, (uint8_t*) MESS->val, (size_t) MESS->len, (uint8_t*) KEY->val);
	if (rc != OQS_SUCCESS) {
        SIG->len=0;
    }
    else{
        SIG->len=OQS_SIG_sphincs_sha2_128f_simple_length_signature;
    }    
}

static bool SPHINCSSHA2128SSIMPLE_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{
    OQS_STATUS rc = OQS_SIG_sphincs_sha2_128s_simple_verify((uint8_t*) CERT->val, CERT->len, (uint8_t*) SIG->val, SIG->len, (uint8_t*) PUBKEY->val);
	if (rc != OQS_SUCCESS) {
        return false;
    }
    return true;  
}

static void SPHINCSSHA2128SSIMPLE_SIGN(octad *KEY,octad *MESS,octad *SIG)
{
    OQS_STATUS rc = OQS_SIG_sphincs_sha2_128s_simple_sign( (uint8_t*) SIG->val, (size_t*) SIG->len, (uint8_t*) MESS->val, (size_t) MESS->len, (uint8_t*) KEY->val);
	if (rc != OQS_SUCCESS) {
        SIG->len=0;
    }
    else{
        SIG->len=OQS_SIG_sphincs_sha2_128s_simple_length_signature;
    }    
}

static bool SPHINCSSHAKE128FSIMPLE_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{
    OQS_STATUS rc = OQS_SIG_sphincs_shake_128f_simple_verify((uint8_t*) CERT->val, CERT->len, (uint8_t*) SIG->val, SIG->len, (uint8_t*) PUBKEY->val);
	if (rc != OQS_SUCCESS) {
        return false;
    }
    return true;  
}

static void SPHINCSSHAKE128FSIMPLE_SIGN(octad *KEY,octad *MESS,octad *SIG)
{
    OQS_STATUS rc = OQS_SIG_sphincs_shake_128f_simple_sign( (uint8_t*) SIG->val, (size_t*) SIG->len, (uint8_t*) MESS->val, (size_t) MESS->len, (uint8_t*) KEY->val);
	if (rc != OQS_SUCCESS) {
        SIG->len=0;
    }
    else{
        SIG->len=OQS_SIG_sphincs_shake_128f_simple_length_signature;
    }    
}
/*
static bool SPHINCSSHA2192FSIMPLE_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{
    OQS_STATUS rc = OQS_SIG_sphincs_sha2_192f_simple_verify((uint8_t*) CERT->val, CERT->len, (uint8_t*) SIG->val, SIG->len, (uint8_t*) PUBKEY->val);
	if (rc != OQS_SUCCESS) {
        return false;
    }
    return true;  
}

static void SPHINCSSHA2192FSIMPLE_SIGN(octad *KEY,octad *MESS,octad *SIG)
{
    OQS_STATUS rc = OQS_SIG_sphincs_sha2_192f_simple_sign( (uint8_t*) SIG->val, (size_t*) SIG->len, (uint8_t*) MESS->val, (size_t) MESS->len, (uint8_t*) KEY->val);
	if (rc != OQS_SUCCESS) {
        SIG->len=0;
    }
    else{
        SIG->len=OQS_SIG_sphincs_sha2_192s_simple_length_signature;
    }    
}
*/
//--------------------------------------------------------------------------
#endif

// RSA 2048-bit PKCS1.5 signature verification
static bool RSA_2048_PKCS15_VERIFY(int sha,octad *CERT,octad *SIG,octad *PUBKEY)
{
    bool res;
    char p1[RFS_RSA2048];
    octet P1={0,sizeof(p1),p1};
    char p2[RFS_RSA2048];
    octet P2={0,sizeof(p2),p2};

    octet MC_CERT=octad_to_octet(CERT);
    octet MC_SIG=octad_to_octet(SIG);
    octet MC_PUBKEY=octad_to_octet(PUBKEY);

    RSA2048::rsa_public_key PK;
    PK.e = 65537; // assuming this!
    RSA2048::RSA_fromOctet(PK.n, &MC_PUBKEY);
    RSA2048::RSA_ENCRYPT(&PK, &MC_SIG, &P2);
    PKCS15(sha, &MC_CERT, &P1);
    res=OCT_comp(&P1, &P2);
    if (!res)
    { // check alternate PKCS1.5 encoding
        PKCS15b(sha, &MC_CERT, &P1);
        res=OCT_comp(&P1, &P2);
    }
    return res;
}

// RSA 4096-bit PKCS1.5 signature verification
static bool RSA_4096_PKCS15_VERIFY(int sha,octad *CERT,octad *SIG,octad *PUBKEY)
{
    bool res;
    char p1[RFS_RSA4096];
    octet P1={0,sizeof(p1),p1};
    char p2[RFS_RSA4096];
    octet P2={0,sizeof(p2),p2};

    octet MC_CERT=octad_to_octet(CERT);
    octet MC_SIG=octad_to_octet(SIG);
    octet MC_PUBKEY=octad_to_octet(PUBKEY);

    RSA4096::rsa_public_key PK;
    PK.e = 65537; // assuming this!
    RSA4096::RSA_fromOctet(PK.n, &MC_PUBKEY);
    RSA4096::RSA_ENCRYPT(&PK, &MC_SIG, &P2);
    PKCS15(sha, &MC_CERT, &P1);
    res=OCT_comp(&P1, &P2);
    if (!res)
    { // check alternate PKCS1.5 encoding
        PKCS15b(sha, &MC_CERT, &P1);
        res=OCT_comp(&P1, &P2);
    }
    return res;
}

static bool RSA_PKCS15_VERIFY(int sha,octad *CERT,octad *SIG,octad *PUBKEY)
{
    if (PUBKEY->len==RFS_RSA2048)
        return RSA_2048_PKCS15_VERIFY(sha,CERT,SIG,PUBKEY);
    if (PUBKEY->len==RFS_RSA4096)
        return RSA_4096_PKCS15_VERIFY(sha,CERT,SIG,PUBKEY);
    return false;
}

// RSA 2048-bit PSS-RSAE signature verification
static bool RSA_2048_PSS_RSAE_VERIFY(int sha,octad *MESS,octad *SIG,octad *PUBKEY)
{
    char p[RFS_RSA2048];
    octet P={0,sizeof(p),p};

    octet MC_MESS=octad_to_octet(MESS);
    octet MC_SIG=octad_to_octet(SIG);
    octet MC_PUBKEY=octad_to_octet(PUBKEY);

    RSA2048::rsa_public_key PK;
    PK.e = 65537;
    RSA2048::RSA_fromOctet(PK.n, &MC_PUBKEY);
    RSA2048::RSA_ENCRYPT(&PK, &MC_SIG, &P);
    if (PSS_VERIFY(sha,&MC_MESS,&P)) 
        return true;
    return false;
}

// RSA 4096-bit PSS-RSAE signature verification
static bool RSA_4096_PSS_RSAE_VERIFY(int sha,octad *MESS,octad *SIG,octad *PUBKEY)
{
    char p[RFS_RSA4096];
    octet P={0,sizeof(p),p};

    octet MC_MESS=octad_to_octet(MESS);
    octet MC_SIG=octad_to_octet(SIG);
    octet MC_PUBKEY=octad_to_octet(PUBKEY);

    RSA4096::rsa_public_key PK;
    PK.e = 65537;
    RSA4096::RSA_fromOctet(PK.n, &MC_PUBKEY);
    RSA4096::RSA_ENCRYPT(&PK, &MC_SIG, &P);
    if (PSS_VERIFY(sha,&MC_MESS,&P)) 
        return true;
    return false;
}

static bool RSA_PSS_RSAE_VERIFY(int sha,octad *MESS,octad *SIG,octad *PUBKEY)
{
    if (PUBKEY->len==RFS_RSA2048)
        return RSA_2048_PSS_RSAE_VERIFY(sha,MESS,SIG,PUBKEY);
    if (PUBKEY->len==RFS_RSA4096)
        return RSA_4096_PSS_RSAE_VERIFY(sha,MESS,SIG,PUBKEY);
    return false;
}

// Curve SECP256R1 elliptic curve ECDSA verification
static bool SECP256R1_ECDSA_VERIFY(int sha,octad *CERT,octad *SIG,octad *PUBKEY)
{
    int res;

    octet MC_CERT=octad_to_octet(CERT);
    octet MC_SIG=octad_to_octet(SIG);
    octet MC_PUBKEY=octad_to_octet(PUBKEY);

    res=NIST256::ECP_PUBLIC_KEY_VALIDATE(&MC_PUBKEY);
    if (res!=0) return false;

    char r[32];
    octet R={0,sizeof(r),r};
    char s[32];
    octet S={0,sizeof(s),s};
    int siglen=SIG->len/2;
    for (int i=0;i<siglen;i++)
    {
        OCT_jbyte(&R,MC_SIG.val[i],1);
        OCT_jbyte(&S,MC_SIG.val[i+siglen],1);
    }    

    res=NIST256::ECP_VP_DSA(sha, &MC_PUBKEY, &MC_CERT, &R, &S);
    if (res!=0) return false;

    return true;
}

// Curve SECP384R1 elliptic curve ECDSA verification

static bool SECP384R1_ECDSA_VERIFY(int sha,octad *CERT,octad *SIG,octad *PUBKEY)
{
    int res;

    octet MC_CERT=octad_to_octet(CERT);
    octet MC_SIG=octad_to_octet(SIG);
    octet MC_PUBKEY=octad_to_octet(PUBKEY);

    res=NIST384::ECP_PUBLIC_KEY_VALIDATE(&MC_PUBKEY);
    if (res!=0) return false;

    char r[48];
    octet R={0,sizeof(r),r};
    char s[48];
    octet S={0,sizeof(s),s};
    int siglen=SIG->len/2;
    for (int i=0;i<siglen;i++)
    {
        OCT_jbyte(&R,MC_SIG.val[i],1);
        OCT_jbyte(&S,MC_SIG.val[i+siglen],1);
    }    

    res=NIST384::ECP_VP_DSA(sha, &MC_PUBKEY, &MC_CERT, &R, &S);
    if (res!=0) return false;

    return true;
}

static bool Ed25519_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{ 
    octet MC_CERT=octad_to_octet(CERT);
    octet MC_SIG=octad_to_octet(SIG);
    octet MC_PUBKEY=octad_to_octet(PUBKEY);
    return Ed25519::EDDSA_VERIFY(false,&MC_PUBKEY,NULL,&MC_CERT,&MC_SIG);
}

static bool Ed448_VERIFY(octad *CERT,octad *SIG,octad *PUBKEY)
{ 
    octet MC_CERT=octad_to_octet(CERT);
    octet MC_SIG=octad_to_octet(SIG);
    octet MC_PUBKEY=octad_to_octet(PUBKEY);
    return Ed448::EDDSA_VERIFY(false,&MC_PUBKEY,NULL,&MC_CERT,&MC_SIG);
}

// Use Curve SECP256R1 ECDSA to digitally sign a message using a private key 
static void SECP256R1_ECDSA_SIGN(int sha,octad *KEY,octad *MESS,octad *SIG)
{
    octet MC_MESS=octad_to_octet(MESS);
    octet MC_KEY=octad_to_octet(KEY);
    octet MC_SIG=octad_to_octet(SIG);

    char r[32];
    octet R={0,sizeof(r),r};
    char s[32];
    octet S={0,sizeof(s),s};

    NIST256::ECP_SP_DSA(sha, &RNG, NULL, &MC_KEY, &MC_MESS, &R, &S);

    OCT_copy(&MC_SIG,&R);
    OCT_joctet(&MC_SIG,&S);

    SIG->len=MC_SIG.len;
}

// Use Curve SECP384R1 ECDSA to digitally sign a message using a private key 
static void SECP384R1_ECDSA_SIGN(int sha,octad *KEY,octad *MESS,octad *SIG)
{
    octet MC_MESS=octad_to_octet(MESS);
    octet MC_KEY=octad_to_octet(KEY);
    octet MC_SIG=octad_to_octet(SIG);

    char r[48];
    octet R={0,sizeof(r),r};
    char s[48];
    octet S={0,sizeof(s),s};

    NIST384::ECP_SP_DSA(sha, &RNG, NULL, &MC_KEY, &MC_MESS, &R, &S);

    OCT_copy(&MC_SIG,&R);
    OCT_joctet(&MC_SIG,&S);

    SIG->len=MC_SIG.len;
}

static void Ed25519_SIGN(octad *KEY,octad *MESS,octad *SIG)
{ 
    octet MC_MESS=octad_to_octet(MESS);
    octet MC_KEY=octad_to_octet(KEY);
    octet MC_SIG=octad_to_octet(SIG);
    Ed25519::EDDSA_SIGNATURE(false,&MC_KEY,NULL,&MC_MESS,&MC_SIG);
    SIG->len=MC_SIG.len;
}

static void Ed448_SIGN(octad *KEY,octad *MESS,octad *SIG)
{ 
    octet MC_MESS=octad_to_octet(MESS);
    octet MC_KEY=octad_to_octet(KEY);
    octet MC_SIG=octad_to_octet(SIG);
    Ed448::EDDSA_SIGNATURE(false,&MC_KEY,NULL,&MC_MESS,&MC_SIG);
    SIG->len=MC_SIG.len;
}

// Use RSA-2048 PSS-RSAE to digitally sign a message using a private key
static void RSA_2048_PSS_RSAE_SIGN(int sha,octad *KEY,octad *MESS,octad *SIG)
{
    int len=KEY->len/5;   // length of p and q
    if (len!=128) return;
    char p[128];
    octet P={len,sizeof(p),p};
    char q[128];
    octet Q={len,sizeof(q),q};
    char dp[128];
    octet DP={len,sizeof(dp),dp};
    char dq[128];
    octet DQ={len,sizeof(dq),dq};
    char c[128];
    octet C={len,sizeof(c),c}; 
    for (int i=0;i<len;i++)
    {
        p[i]=KEY->val[i];
        q[i]=KEY->val[i+len];
        dp[i]=KEY->val[i+2*len];
        dq[i]=KEY->val[i+3*len];
        c[i]=KEY->val[i+4*len];
    }
    RSA2048::rsa_private_key SK;
    char enc[256];
    octet ENC={0,sizeof(enc),enc};
    RSA2048::RSA_PRIVATE_KEY_FROM_OPENSSL(&P,&Q,&DP,&DQ,&C,&SK);

    octet MC_MESS=octad_to_octet(MESS);
    octet MC_SIG=octad_to_octet(SIG);

    PSS_ENCODE(sha, &MC_MESS, &RNG, &ENC);
    RSA2048::RSA_DECRYPT(&SK,&ENC,&MC_SIG);
    SIG->len=MC_SIG.len;
}

// Use RSA-4096 PSS-RSAE to digitally sign a message using a private key
static void RSA_4096_PSS_RSAE_SIGN(int sha,octad *KEY,octad *MESS,octad *SIG)
{
    int len=KEY->len/5;   // length of p and q
    if (len!=256) return;
    char p[256];
    octet P={len,sizeof(p),p};
    char q[256];
    octet Q={len,sizeof(q),q};
    char dp[256];
    octet DP={len,sizeof(dp),dp};
    char dq[256];
    octet DQ={len,sizeof(dq),dq};
    char c[256];
    octet C={len,sizeof(c),c}; 
    for (int i=0;i<len;i++)
    {
        p[i]=KEY->val[i];
        q[i]=KEY->val[i+len];
        dp[i]=KEY->val[i+2*len];
        dq[i]=KEY->val[i+3*len];
        c[i]=KEY->val[i+4*len];
    }
    RSA4096::rsa_private_key SK;
    char enc[512];
    octet ENC={0,sizeof(enc),enc};

    octet MC_MESS=octad_to_octet(MESS);
    octet MC_SIG=octad_to_octet(SIG);

    RSA4096::RSA_PRIVATE_KEY_FROM_OPENSSL(&P,&Q,&DP,&DQ,&C,&SK);

    PSS_ENCODE(sha, &MC_MESS, &RNG, &ENC);
    RSA4096::RSA_DECRYPT(&SK,&ENC,&MC_SIG);
    SIG->len=MC_SIG.len;
}

static void RSA_PSS_RSAE_SIGN(int sha,octad *KEY,octad *MESS,octad *SIG)
{
    int len=KEY->len/5;
    if (len==128) RSA_2048_PSS_RSAE_SIGN(sha,KEY,MESS,SIG);
    if (len==256) RSA_4096_PSS_RSAE_SIGN(sha,KEY,MESS,SIG);
}

// RFC8446:     "A TLS-compliant application MUST support digital signatures with
//              rsa_pkcs1_sha256 (for certificates), rsa_pss_rsae_sha256 (for
//              CertificateVerify and certificates), and ecdsa_secp256r1_sha256."

// SAL signature verification
bool SAL_tlsSignatureVerify(int sigAlg,octad *BUFF,octad *SIG,octad *PUBKEY)
{
    bool result;
    //double start,elapsed;
    //start = millis();
    uint64_t start, end;
    start = _rdtsc();
    switch (sigAlg) {
        case RSA_PKCS1_SHA256 :
            result= RSA_PKCS15_VERIFY(32,BUFF,SIG,PUBKEY);
            break;
        case ECDSA_SECP256R1_SHA256 :
            result= SECP256R1_ECDSA_VERIFY(32,BUFF,SIG,PUBKEY); 
            break;
        case ECDSA_SECP256R1_SHA384 :
            result= SECP256R1_ECDSA_VERIFY(48,BUFF,SIG,PUBKEY); 
            break;
        case RSA_PKCS1_SHA384 :
            result= RSA_PKCS15_VERIFY(48,BUFF,SIG,PUBKEY);
            break;        
        case ECDSA_SECP384R1_SHA384 :
            result= SECP384R1_ECDSA_VERIFY(48,BUFF,SIG,PUBKEY);
            break;
        case RSA_PKCS1_SHA512 :
            result= RSA_PKCS15_VERIFY(64,BUFF,SIG,PUBKEY);
            break;        
        case RSA_PSS_RSAE_SHA256:
            result= RSA_PSS_RSAE_VERIFY(32,BUFF,SIG,PUBKEY);
            break;
        case ED25519:
            result= Ed25519_VERIFY(BUFF,SIG,PUBKEY);
            break;
        case ED448:
            result= Ed448_VERIFY(BUFF,SIG,PUBKEY);
            break;

// JS doplnenie novych PQ algoritmov
#if CRYPTO_SETTING>=POST_QUANTUM
        case DILITHIUM2:
            result= DILITHIUM2_VERIFY(BUFF,SIG,PUBKEY);
            break;
        case DILITHIUM3:
            result= DILITHIUM3_VERIFY(BUFF,SIG,PUBKEY);
            break;
        case DILITHIUM5:
            result= DILITHIUM5_VERIFY(BUFF,SIG,PUBKEY);
            break;
        case MLDSA44:
            result= MLDSA44_VERIFY(BUFF,SIG,PUBKEY);
            break;
        case MLDSA65:
            result= MLDSA65_VERIFY(BUFF,SIG,PUBKEY);
            break;
        case MLDSA87:
            result= MLDSA87_VERIFY(BUFF,SIG,PUBKEY);
            break;
        case FALCON512:
            result= FALCON512_VERIFY(BUFF,SIG,PUBKEY);
            break;
        case FALCON1024:
            result= FALCON1024_VERIFY(BUFF,SIG,PUBKEY);
            break;
        case SPHINCSSHA2128FSIMPLE:
            result= SPHINCSSHA2128FSIMPLE_VERIFY(BUFF,SIG,PUBKEY);
            break;
        case SPHINCSSHA2128SSIMPLE:
            result= SPHINCSSHA2128SSIMPLE_VERIFY(BUFF,SIG,PUBKEY);
            break;
        case SPHINCSSHAKE128FSIMPLE:
            result= SPHINCSSHAKE128FSIMPLE_VERIFY(BUFF,SIG,PUBKEY);
            break;
        /*
        case SPHINCSSHA2192FSIMPLE:
            result= SPHINCSSHA2192FSIMPLE_VERIFY(BUFF,SIG,PUBKEY);
            break;
        */
#endif
        default:
            result=false;
    }
    //elapsed=(millis()-start);
    //printf("        Verification= %.2lf ms\n", elapsed);
    
    end = _rdtsc();
    uint64_t elapsed_ticks = end - start;
    double elapsed_microseconds = (double)elapsed_ticks;

    // JS 3.8 je frekvencia pouziteho CPU, 1e6 transformuje hodnotu citacu na ms
    printf("Elapsed time: %.2f microseconds\n", elapsed_microseconds / (3.8 * 1e6));
    return result;
}

// Form Transcript Signature 
void SAL_tlsSignature(int sigAlg,octad *KEY,octad *TRANS,octad *SIG)
{ // probably need to support more cases
    //int start,elapsed;
    //start = millis();
    switch (sigAlg)
    {
    case RSA_PSS_RSAE_SHA256:
        RSA_PSS_RSAE_SIGN(32,KEY,TRANS,SIG);
        break;
    case ED25519:
        Ed25519_SIGN(KEY,TRANS,SIG);
        break;
    case ED448:
        Ed448_SIGN(KEY,TRANS,SIG);
        break;
    case ECDSA_SECP256R1_SHA256:
        SECP256R1_ECDSA_SIGN(32,KEY,TRANS,SIG);
        break;
    case ECDSA_SECP256R1_SHA384:
        SECP256R1_ECDSA_SIGN(48,KEY,TRANS,SIG);
        break;
    case ECDSA_SECP384R1_SHA384:
        SECP384R1_ECDSA_SIGN(48,KEY,TRANS,SIG);
        break;

// JS doplnenie novych PQ algoritmov
#if CRYPTO_SETTING>=POST_QUANTUM
    case DILITHIUM2:
        DILITHIUM2_SIGN(KEY,TRANS,SIG);
        break;
    case DILITHIUM3:
        DILITHIUM3_SIGN(KEY,TRANS,SIG);
        break;
    case DILITHIUM5:
        DILITHIUM5_SIGN(KEY,TRANS,SIG);
        break;
    case MLDSA44:
        MLDSA44_SIGN(KEY,TRANS,SIG);
        break;
    case MLDSA65:
        MLDSA65_SIGN(KEY,TRANS,SIG);
        break;
    case MLDSA87:
        MLDSA87_SIGN(KEY,TRANS,SIG);
        break;
    case FALCON512:
        FALCON512_SIGN(KEY,TRANS,SIG);
            break;
    case FALCON1024:
        FALCON1024_SIGN(KEY,TRANS,SIG);
        break;
    case SPHINCSSHA2128FSIMPLE:
        SPHINCSSHA2128FSIMPLE_SIGN(KEY,TRANS,SIG);
        break;
    case SPHINCSSHA2128SSIMPLE:
        SPHINCSSHA2128SSIMPLE_SIGN(KEY,TRANS,SIG);
        break;
    case SPHINCSSHAKE128FSIMPLE:
        SPHINCSSHAKE128FSIMPLE_SIGN(KEY,TRANS,SIG);
        break;
    /* 
    case SPHINCSSHA2192FSIMPLE:
        SPHINCSSHA2192FSIMPLE_SIGN(KEY,TRANS,SIG);
        break;  
    */
#endif
    }
    //elapsed=(millis()-start);
    //printf("        Signature= %d\n", elapsed);
}