/*
* JS update 14.04.2024
* Doplneny vypis pre algoritmy ML-KEM a ML-DSA
*/

/*
* JS update 29.02.2024
* Doplnene vypisy pre PQ algoritmy
* Odstranene funkcie pre ARDUINO
*/

// 
// Log protocol progress
//
#include "tls_logger.h"
#include <stdio.h>


// all terminal output redirected here
void myprintf(char *s)
{
    printf("%s",s);
}

// log debug string and info or octad
// if string is not NULL, output info, with format in string
// if O is not null, output octad in hex.

void log(int logit,char *preamble,char *string,unsign32 info,octad *O)
{
    if (logit>VERBOSITY) return;

#if VERBOSITY>IO_NONE    
    char w[16];
    myprintf(preamble);

    if (O!=NULL)
    {
        char buff[LOG_OUTPUT_TRUNCATION+8];
        bool res=OCT_output_hex(O,LOG_OUTPUT_TRUNCATION,buff);
        sprintf(w,"(%d) ",O->len);
        myprintf(w);
        myprintf(buff);
        if (!res) myprintf((char *)" (truncated)");
        myprintf((char *)"\n");
        return;
    }    

    if (string!=NULL)
    { // if its bigger than 6 its not a format descriptor ???
        if (strlen(string)>6)
            myprintf(string);
        else
        {
            sprintf(w,string,info);
            myprintf(w);
        }
        myprintf((char *)"\n");
        return;
    }
 
#endif
}

static void nameCipherSuite(int cipher_suite)
{
    switch (cipher_suite)
    {
    case TLS_AES_128_GCM_SHA256:
        myprintf((char *)"TLS_AES_128_GCM_SHA256\n");
        break;
    case TLS_AES_256_GCM_SHA384:
        myprintf((char *)"TLS_AES_256_GCM_SHA384\n");   
        break;
    case TLS_CHACHA20_POLY1305_SHA256:
        myprintf((char *)"TLS_CHACHA20_POLY1305_SHA256\n");   
        break;
    default:
        myprintf((char *)"Non-standard\n");   
        break;
    }
}

void logCipherSuite(int cipher_suite)
{
#if VERBOSITY >= IO_DEBUG
    log(IO_DEBUG,(char *)"Cipher Suite is ",NULL,0,NULL);
    nameCipherSuite(cipher_suite);
#endif
}

// JS doplnene nazvy PQ algoritmov
static void nameKeyExchange(int kex)
{
#if VERBOSITY >= IO_PROTOCOL
    switch (kex)
    {
    case X25519:
        myprintf((char *)"X25519\n");
        break;
    case SECP256R1:
        myprintf((char *)"SECP256R1\n");   
        break;
    case SECP384R1:
        myprintf((char *)"SECP384R1\n");   
        break;
    case KYBER768:
        myprintf((char *)"KYBER768\n");
        break;
    case KYBER512:
        myprintf((char *)"KYBER512\n");
        break;
    case KYBER1024:
        myprintf((char *)"KYBER1024\n");
        break;
    case MLKEM512:
        myprintf((char *)"MLKEM512\n");
        break;
    case MLKEM768:
        myprintf((char *)"MLKEM768\n");
        break;
    case MLKEM1024:
        myprintf((char *)"MLKEM1024\n");
        break;
    case BIKEL1:
        myprintf((char *)"BIKEL1\n");
        break;
    case BIKEL3:
        myprintf((char *)"BIKEL3\n");
        break;
    case BIKEL5:
        myprintf((char *)"BIKEL5\n");
        break;
    case HQC128:
        myprintf((char *)"HQC128\n");
        break;
    case HQC192:
        myprintf((char *)"HQC192\n");
        break;
    case HQC256:
        myprintf((char *)"HQC256\n");
        break;
    case FRODO640AES:
        myprintf((char *)"FRODO640AES\n");
        break;
    case FRODO640SHAKE:
        myprintf((char *)"FRODO640SHAKE\n");
        break;
    case FRODO976AES:
        myprintf((char *)"FRODO976AES\n");
        break;
    case FRODO976SHAKE:
        myprintf((char *)"FRODO976SHAKE\n");
        break;
    case FRODO1344AES:
        myprintf((char *)"FRODO1344AES\n");
        break;
    case FRODO1344SHAKE:
        myprintf((char *)"FRODO1344SHAKE\n");
        break;
    case HYBRID_KX:
        myprintf((char *)"KYBER + X25519\n");
        break;
    default:
        myprintf((char *)"Non-standard\n");   
        break;
    }
#endif
}

void logKeyExchange(int kex)
{
    log(IO_PROTOCOL,(char *)"Key Exchange Group is ",NULL,0,NULL);
    nameKeyExchange(kex);
}

// JS doplnene nazvy PQ algoritmov
static void nameSigAlg(int sigAlg)
{
#if VERBOSITY >= IO_PROTOCOL
    switch (sigAlg)
    {
    case ECDSA_SECP256R1_SHA256:
        myprintf((char *)"ECDSA_SECP256R1_SHA256\n");
        break;
    case RSA_PSS_RSAE_SHA256:
        myprintf((char *)"RSA_PSS_RSAE_SHA256\n");   
        break;
    case RSA_PKCS1_SHA256:
        myprintf((char *)"RSA_PKCS1_SHA256\n");   
        break;
    case ECDSA_SECP384R1_SHA384:
        myprintf((char *)"ECDSA_SECP384R1_SHA384\n");
        break;
    case RSA_PSS_RSAE_SHA384:
        myprintf((char *)"RSA_PSS_RSAE_SHA384\n");   
        break;
    case RSA_PKCS1_SHA384:
        myprintf((char *)"RSA_PKCS1_SHA384\n");   
        break;
    case RSA_PSS_RSAE_SHA512:
        myprintf((char *)"RSA_PSS_RSAE_SHA512\n");   
        break;
    case RSA_PKCS1_SHA512:
        myprintf((char *)"RSA_PKCS1_SHA512\n");   
        break;
    case ED25519:
        myprintf((char *)"ED25519\n");   
        break;
//------------------------------------------------
    case DILITHIUM2:
        myprintf((char *)"DILITHIUM2\n");   
        break;
    case DILITHIUM3:
        myprintf((char *)"DILITHIUM3\n");   
        break;
    case DILITHIUM5:
        myprintf((char *)"DILITHIUM5\n");   
        break;
    case MLDSA44:
        myprintf((char *)"MLDSA44\n");   
        break;
    case MLDSA65:
        myprintf((char *)"MLDSA65\n");   
        break;
    case MLDSA87:
        myprintf((char *)"MLDSA87\n");   
        break;
    case FALCON512:
        myprintf((char *)"FALCON512\n");   
        break;
    case FALCON1024:
        myprintf((char *)"FALCON1024\n");   
        break;
    case SPHINCSSHA2128FSIMPLE:
        myprintf((char *)"SPHINCS_SHA2_128F_SIMPLE\n");   
        break;
    case SPHINCSSHA2128SSIMPLE:
        myprintf((char *)"SPHINCS_SHA2_128S_SIMPLE\n");   
        break;
    case SPHINCSSHAKE128FSIMPLE:
        myprintf((char *)"SPHINCS_SHAKE_128F_SIMPLE\n");   
        break;
//------------------------------------------------
    case DILITHIUM2_P256:
        myprintf((char *)"DILITHIUM2 + P256\n");   
        break;
    default:
        myprintf((char *)"Non-standard\n");   
        break;
    }
#endif
}

void logSigAlg(int sigAlg)
{
    log(IO_PROTOCOL,(char *)"Signature Algorithm is ",NULL,0,NULL);
    nameSigAlg(sigAlg);
}

// log Encrypted Extensions Responses
void logEncExt(ee_status *expected,ee_status *received)
{
#if VERBOSITY >= IO_PROTOCOL
    if (expected->early_data)
    {
        if (received->early_data)
        {
            log(IO_PROTOCOL,(char *)"Early Data Accepted\n",NULL,0,NULL);
        } else {
            log(IO_PROTOCOL,(char *)"Early Data was NOT Accepted\n",NULL,0,NULL);
        }
    }
#endif
#if VERBOSITY >= IO_DEBUG
    if (expected->alpn)
    {
        if (received->alpn)
        {
             log(IO_DEBUG,(char *)"ALPN extension acknowledged by server\n",NULL,0,NULL);
        
        } else {
            log(IO_DEBUG,(char *)"Warning - ALPN extension NOT acknowledged by server\n",NULL,0,NULL);
        }
    }

    if (expected->server_name)
    {
        if (received->server_name)
        {
            log(IO_DEBUG,(char *)"Server Name acknowledged\n",NULL,0,NULL);
        } else {
            log(IO_DEBUG,(char *)"Server Name NOT acknowledged\n",NULL,0,NULL);
        }
    }
    if (expected->max_frag_length)
    {
        if (received->max_frag_length)
        {
            log(IO_DEBUG,(char *)"Max frag length request acknowledged\n",NULL,0,NULL);
        } else {
            log(IO_DEBUG,(char *)"Max frag length request NOT acknowledged\n",NULL,0,NULL);
        }
    }
#endif
}

// log server hello outputs
void logServerHello(int cipher_suite,int pskid,octad *PK,octad *CK)
{
#if VERBOSITY >= IO_DEBUG
    log(IO_DEBUG,(char *)"Parsing serverHello\n",NULL,0,NULL);
    logCipherSuite(cipher_suite);
    if (pskid>=0) log(IO_DEBUG,(char *)"PSK Identity= ",(char *)"%d",pskid,NULL);
    if (PK->len>0) {
        log(IO_DEBUG,(char *)"Server Public Key= ",NULL,0,PK);//OCT_output(PK);
    }
    if (CK->len>0) {
        log(IO_DEBUG,(char *)"Cookie= ",NULL,0,CK); //OCT_output(CK);
    }
    log(IO_DEBUG,(char *)"\n",NULL,0,NULL);
#endif
}

// log ticket details
void logTicket(ticket *T)
{
#if VERBOSITY >= IO_DEBUG
    log(IO_DEBUG,(char *)"\nParsing Ticket\n",NULL,0,NULL);
    log(IO_DEBUG,(char *)"Ticket = ",NULL,0,&T->TICK); 
    unsign32 minutes=T->lifetime/60;
    log(IO_DEBUG,(char *)"life time in minutes = ",(char *)"%d",minutes,NULL);
    log(IO_DEBUG,(char *)"PSK = ",NULL,0,&T->PSK); 
    log(IO_DEBUG,(char *)"max_early_data = ",(char *)"%d",T->max_early_data,NULL);
    log(IO_DEBUG,(char *)"\n",NULL,0,NULL);
#endif
}

// log a certificate in base64

void logCert(octad *CERT)
{
    char b[5004];
    log(IO_DEBUG,(char *)"-----BEGIN CERTIFICATE----- \n",NULL,0,NULL);
#if VERBOSITY >= IO_DEBUG
    OCT_output_base64(CERT,5000,b);
#endif
    log(IO_DEBUG,(char *)"",b,0,NULL);
    log(IO_DEBUG,(char *)"-----END CERTIFICATE----- \n",NULL,0,NULL);
}

// Construct Distinguished Name from DER encoding
static int make_dn(octad *DN,octad *DER) {
    int i,c,len,n=0;
    DN->val[n++]='{';
    c=X509_find_entity_property(DER,&X509_MN,0,&len);
    for (i=0;i<len;i++) {
        DN->val[n++]=DER->val[c+i];
    }

    DN->val[n++]=',';
    c=X509_find_entity_property(DER,&X509_UN,0,&len);
    for (i=0;i<len;i++) {
        DN->val[n++]=DER->val[c+i];
    }

    DN->val[n++]=',';
    c=X509_find_entity_property(DER,&X509_ON,0,&len);
    for (i=0;i<len;i++) {
        DN->val[n++]=DER->val[c+i];
    }

    DN->val[n++]=',';
    c=X509_find_entity_property(DER,&X509_CN,0,&len);
    for (i=0;i<len;i++) {
        DN->val[n++]=DER->val[c+i];
    }
    DN->val[n++]='}';
    DN->val[n++]=0;
    DN->len=n;
    return n;
}

// log certificate details
void logCertDetails(octad *PUBKEY,pktype pk,octad *SIG,pktype sg,octad *ISSUER,octad *SUBJECT)
{
#if VERBOSITY >= IO_DEBUG
    log(IO_DEBUG,(char *)"Parsing Certificate\n",NULL,0,NULL);
    log(IO_DEBUG,(char *)"Signature on Certificate is ",NULL,0,SIG); 
    if (sg.type==X509_ECC)
    {
        log(IO_DEBUG,(char *)"ECDSA signature ",NULL,0,NULL);
        if (sg.curve==USE_NIST256)
            log(IO_DEBUG,(char *)"Curve is SECP256R1\n",NULL,0,NULL);
        if (sg.curve==USE_NIST384)
            log(IO_DEBUG,(char *)"Curve is SECP384R1\n",NULL,0,NULL);
        if (sg.curve==USE_NIST521)
            log(IO_DEBUG,(char *)"Curve is SECP521R1\n",NULL,0,NULL);
        if (sg.hash == X509_H256) log(IO_DEBUG,(char *)"Hashed with SHA256\n",NULL,0,NULL);
        if (sg.hash == X509_H384) log(IO_DEBUG,(char *)"Hashed with SHA384\n",NULL,0,NULL);
        if (sg.hash == X509_H512) log(IO_DEBUG,(char *)"Hashed with SHA512\n",NULL,0,NULL);
    }
    if (sg.type==X509_ECD)
    {
       log(IO_DEBUG,(char *)"EDDSA signature ",NULL,0,NULL);
        if (sg.curve==USE_ED25519)
            log(IO_DEBUG,(char *)"Curve is ED25519\n",NULL,0,NULL);
        if (sg.curve==USE_ED448)
            log(IO_DEBUG,(char *)"Curve is ED448\n",NULL,0,NULL);
    }
    if (sg.type==X509_RSA)
        log(IO_DEBUG,(char *)"RSA signature of length ",(char *)"%d",sg.curve,NULL);

    log(IO_DEBUG,(char *)"Public key from Certificate is ",NULL,0,PUBKEY); 
    if (pk.type==X509_ECC)
    {
        log(IO_DEBUG,(char *)"ECC public key ",NULL,0,NULL);
        if (pk.curve==USE_NIST256)
            log(IO_DEBUG,(char *)"Curve is SECP256R1\n",NULL,0,NULL);
        if (pk.curve==USE_NIST384)
            log(IO_DEBUG,(char *)"Curve is SECP384R1\n",NULL,0,NULL);
        if (pk.curve==USE_NIST521)
            log(IO_DEBUG,(char *)"Curve is SECP521R1\n",NULL,0,NULL);
    }
    if (pk.type==X509_RSA)
        log(IO_DEBUG,(char *)"Certificate public key is RSA of length ",(char *)"%d",pk.curve,NULL);
    
    char dn[256];
    octad DN={0,sizeof(dn),dn};
    make_dn(&DN,ISSUER);

    log(IO_DEBUG,(char *)"Issuer is  ",(char *)DN.val,0,NULL);
    make_dn(&DN,SUBJECT);
    log(IO_DEBUG,(char *)"Subject is ",(char *)DN.val,0,NULL);
    //log(IO_DEBUG,(char *)"Issuer is  ",(char *)ISSUER->val,0,NULL);
    //log(IO_DEBUG,(char *)"Subject is ",(char *)SUBJECT->val,0,NULL);
#endif
}

// log alert
void logAlert(int detail)
{
#if VERBOSITY >= IO_PROTOCOL
    switch (detail)
    {
    case 0 :
        log(IO_PROTOCOL,(char *)"Close notify\n",NULL,0,NULL);
        break;
    case 10 :
        log(IO_PROTOCOL,(char *)"Unexpected Message\n",NULL,0,NULL);
        break;
    case 20 :
        log(IO_PROTOCOL,(char *)"Bad record mac\n",NULL,0,NULL);
        break;
    case 22 :
        log(IO_PROTOCOL,(char *)"Record overflow\n",NULL,0,NULL);
        break;
    case 40 :
        log(IO_PROTOCOL,(char *)"Handshake Failure (not TLS1.3?)\n",NULL,0,NULL);
        break;
    case 42 :
        log(IO_PROTOCOL,(char *)"Bad certificate\n",NULL,0,NULL);
        break;
    case 43 :
        log(IO_PROTOCOL,(char *)"Unsupported certificate\n",NULL,0,NULL);
        break;
    case 44 :
        log(IO_PROTOCOL,(char *)"Certificate revoked\n",NULL,0,NULL);
        break;
    case 45 :
        log(IO_PROTOCOL,(char *)"Certificate expired\n",NULL,0,NULL);
        break;
    case 46 :
        log(IO_PROTOCOL,(char *)"Certificate unknown\n",NULL,0,NULL);
        break;
    case 47 :
        log(IO_PROTOCOL,(char *)"Illegal parameter\n",NULL,0,NULL);
        break;
    case 48 :
        log(IO_PROTOCOL,(char *)"Unknown CA\n",NULL,0,NULL);
        break;
    case 49 :
        log(IO_PROTOCOL,(char *)"Access denied\n",NULL,0,NULL);
        break;
    case 50 :
        log(IO_PROTOCOL,(char *)"Decode error\n",NULL,0,NULL);
        break;
    case 51 :
        log(IO_PROTOCOL,(char *)"Decrypt error\n",NULL,0,NULL);
        break;
    case 70 :
        log(IO_PROTOCOL,(char *)"Protocol version\n",NULL,0,NULL);
        break;
    case 71 :
        log(IO_PROTOCOL,(char *)"Insufficient security\n",NULL,0,NULL);
        break;
    case 80 :
        log(IO_PROTOCOL,(char *)"Internal error\n",NULL,0,NULL);
        break;
    case 86 :
        log(IO_PROTOCOL,(char *)"Inappropriate fallback\n",NULL,0,NULL);
        break;
    case 90 :
        log(IO_PROTOCOL,(char *)"User cancelled\n",NULL,0,NULL);
        break;
    case 109 :
        log(IO_PROTOCOL,(char *)"Missing Extension\n",NULL,0,NULL);
        break;
    case 110 :
        log(IO_PROTOCOL,(char *)"Unsupported Extension\n",NULL,0,NULL);
        break;
    case 112 :
        log(IO_PROTOCOL,(char *)"Unrecognised name\n",NULL,0,NULL);
        break;
    case 113 :
        log(IO_PROTOCOL,(char *)"Bad certificate status response\n",NULL,0,NULL);
        break;
    case 115 :
        log(IO_PROTOCOL,(char *)"Unknown PSK identity \n",NULL,0,NULL);
        break;
    case 116 :
        log(IO_PROTOCOL,(char *)"Certificate required\n",NULL,0,NULL);
        break;
    case 120 :
        log(IO_PROTOCOL,(char *)"No application protocol\n",NULL,0,NULL);
        break;
    default:
        log(IO_PROTOCOL,(char *)"Unrecognised alert\n",NULL,0,NULL);
        break;
    }
#endif
}

// process server function return
void logServerResponse(ret r)
{
    int rtn=r.err;
    if (rtn==0) return;
#if VERBOSITY >= IO_DEBUG
    if (rtn<0)
    { // fatal errors - after logging we will send a server alert and close connection
        switch (rtn)
        { 
        case NOT_TLS1_3:
            log(IO_DEBUG,(char *)"Not TLS1.3\n",NULL,0,NULL);
            break;
        case ID_MISMATCH:
            log(IO_DEBUG,(char *)"Identity Mismatch\n",NULL,0,NULL);
            break;
        case UNRECOGNIZED_EXT:
            log(IO_DEBUG,(char *)"Unrecognised Extension\n",NULL,0,NULL);
            break;
        case BAD_HELLO:
            log(IO_DEBUG,(char *)"Malformed Hello\n",NULL,0,NULL);
            break;
        case WRONG_MESSAGE:
            log(IO_DEBUG,(char *)"Message received out-of-order\n",NULL,0,NULL);
            break;
        case BAD_CERT_CHAIN:
            log(IO_DEBUG,(char *)"Bad Certificate Chain\n",NULL,0,NULL);
            break;
        case MISSING_REQUEST_CONTEXT:
            log(IO_DEBUG,(char *)"Missing Request Context\n",NULL,0,NULL);
            break;
        case AUTHENTICATION_FAILURE:
            log(IO_DEBUG,(char *)"Authentication Failure\n",NULL,0,NULL);
            break;
        case BAD_RECORD:
            log(IO_DEBUG,(char *)"Malformed Record received (max size exceeded?)\n",NULL,0,NULL);
            break;
        case BAD_TICKET:
            log(IO_DEBUG,(char *)"Malformed Ticket received\n",NULL,0,NULL);
            break;
        case NOT_EXPECTED:
            log(IO_DEBUG,(char *)"Unexpected message/extension\n",NULL,0,NULL);
            break;
        case CA_NOT_FOUND:
            log(IO_DEBUG,(char *)"Certificate Authority not found\n",NULL,0,NULL);
            break;
        case CERT_OUTOFDATE:
            log(IO_DEBUG,(char *)"Certificate is out of date\n",NULL,0,NULL);
            break;
        case MEM_OVERFLOW:
            log(IO_DEBUG,(char *)"Memory overflow\n",NULL,0,NULL);
            break;
        case FORBIDDEN_EXTENSION:
            log(IO_DEBUG,(char *)"Forbidden extension found\n",NULL,0,NULL);
            break;
        case MAX_EXCEEDED:
            log(IO_DEBUG,(char *)"Maximum record size exceeded\n",NULL,0,NULL);
            break;
        case CERT_VERIFY_FAIL:
            log(IO_DEBUG,(char *)"Certificate verification failure\n",NULL,0,NULL);
            break;
        case BAD_HANDSHAKE:
            log(IO_DEBUG,(char *)"Handshake protocol failure\n",NULL,0,NULL);
            break;
        case BAD_REQUEST_UPDATE:
            log(IO_DEBUG,(char *)"Bad Key update request\n",NULL,0,NULL);
            break;
        case MISSING_EXTENSIONS:
            log(IO_DEBUG,(char *)"Some extension(s) are missing\n",NULL,0,NULL);
            break;
        case BAD_MESSAGE:
            log(IO_DEBUG,(char *)"Malformed Message received\n",NULL,0,NULL);
            break;
        case EMPTY_CERT_CHAIN:
            log(IO_DEBUG,(char *)"Client Certificate required\n",NULL,0,NULL);
            break;
        default:
            log(IO_DEBUG,(char *)"Unknown Error\n",NULL,0,NULL);
            break;
        }
    } else { // server response requiring client action 
        switch (rtn)
        {
        case TIMED_OUT :
            log(IO_DEBUG,(char *)"Time Out\n",NULL,0,NULL);
            break;
        case ALERT :
            log(IO_DEBUG,(char *)"Alert received from server\n",NULL,0,NULL);  // received an alert 
            break;
        default: break;
        }
    }
#endif
}
