//
// TLS1.3 crypto support functions (hashing, KDFs etc)
//

#include "tls_keys_calc.h"
#include "tls_logger.h"
#include "tls_x509.h"

// Initialise transcript hash
void initTranscriptHash(TLS_session *session)
{
    int hashtype=SAL_hashType(session->cipher_suite);
    SAL_hashInit(hashtype,&session->tlshash);
}

// Add octad to transcript hash 
void runningHash(TLS_session *session,octad *O)
{
    SAL_hashProcessArray(&session->tlshash,O->val,O->len);
}

// Add IO buffer to transcript hash
void runningHashIO(TLS_session *session)
{
    SAL_hashProcessArray(&session->tlshash,session->IBUFF.val,session->ptr);
}

// Shift octad left - rewind IO buffer to start 
void rewindIO(TLS_session *session)
{
    OCT_shift_left(&session->IBUFF,session->ptr);  
    session->ptr=0;
}

void runningHashIOrewind(TLS_session *session)
{
    runningHashIO(session);
    rewindIO(session);
}

// Output transcript hash 
void transcriptHash(TLS_session *session,octad *O)
{
    O->len=SAL_hashOutput(&session->tlshash,O->val); 
}

// special case handling for first clientHello after retry request
void runningSyntheticHash(TLS_session *session,octad *O,octad *E)
{
    int htype=session->tlshash.htype; 
    unihash rhash;
    char hh[TLS_MAX_HASH];
    octad HH={0,sizeof(hh),hh};

    SAL_hashInit(htype,&rhash); 
 // RFC 8446 - "special synthetic message"
    SAL_hashProcessArray(&rhash,O->val,O->len);
    SAL_hashProcessArray(&rhash,E->val,E->len);
    HH.len=SAL_hashOutput(&rhash,HH.val);
    
    char t[4];
    t[0]=MESSAGE_HASH;
    t[1]=t[2]=0;
    t[3]=SAL_hashLen(htype);
    SAL_hashProcessArray(&session->tlshash,t,4);
    
    runningHash(session,&HH);
}

// Initialise crypto context (Key,IV, Record number)
void initCryptoContext(crypto *C)
{
    C->active=false;

    C->K.len = 0;
    C->K.max = TLS_MAX_KEY;
    C->K.val = C->k;

    C->IV.len = 0;
    C->IV.max = 12;
    C->IV.val = C->iv;

    C->suite=TLS_AES_128_GCM_SHA256; // default
    C->record=0;
    C->taglen=16;  // default
}

// Fill a crypto context with new key/IV
void updateCryptoContext(crypto *C,octad *K,octad *IV)
{ 
    C->active=true;
    OCT_copy(&(C->K),K);
    OCT_copy(&(C->IV),IV);
    C->record=0;
}

//  increment record, and update IV
void incrementCryptoContext(crypto *C)
{ 
    unsigned char b[4];  
    b[3] = (unsigned char)(C->record);
    b[2] = (unsigned char)(C->record >> 8);
    b[1] = (unsigned char)(C->record >> 16);
    b[0] = (unsigned char)(C->record >> 24);
    for (int i=0;i<4;i++)
        C->IV.val[8+i]^=b[i];  // revert to original IV
    C->record++;
    b[3] = (unsigned char)(C->record);
    b[2] = (unsigned char)(C->record >> 8);
    b[1] = (unsigned char)(C->record >> 16);
    b[0] = (unsigned char)(C->record >> 24);
    for (int i=0;i<4;i++)
        C->IV.val[8+i]^=b[i];  // advance to new IV
}

// create expanded HKDF label LB from label and context
static void hkdfLabel(octad *LB,int length,octad *Label,octad *CTX)
{
    OCT_append_int(LB,length,2);                    // 2
    OCT_append_byte(LB,(char)(6+Label->len),1);        // 1
    OCT_append_string(LB,(char *)"tls13 ");            // 6
    OCT_append_octad(LB,Label);                        // Label->len
    if (CTX!=NULL)
    {
        OCT_append_byte(LB, (char)(CTX->len), 1);    // 1
        OCT_append_octad(LB,CTX);                    // CTX->len
    } else {
        OCT_append_byte(LB,0,1);                    // 1
    }
}

// HKDF extension for TLS1.3
static void hkdfExpandLabel(int htype,octad *OKM,int olen,octad *PRK,octad *Label,octad *CTX)
{
    char hl[TLS_MAX_HASH+24];
    octad HL={0,sizeof(hl),hl};
    hkdfLabel(&HL,olen,Label,CTX);
    SAL_hkdfExpand(htype,olen,OKM,PRK,&HL);
}

// create verification data
void deriveVeriferData(int htype,octad *CF,octad *CHTS,octad *H)
{
    char fk[TLS_MAX_HASH];
    octad FK = {0,sizeof(fk),fk};
    char info[10];
    octad INFO = {0,sizeof(info),info};
    int hlen=SAL_hashLen(htype);
    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"finished");
    hkdfExpandLabel(htype,&FK,hlen,CHTS,&INFO,NULL); 
    SAL_hmac(htype,CF,&FK,H);
}

// check verification data
bool checkVeriferData(int htype,octad *SF,octad *SHTS,octad *H)
{
    char vd[TLS_MAX_HASH];
    octad VD = {0,sizeof(vd),vd};
    deriveVeriferData(htype,&VD,SHTS,H);
    return OCT_compare(SF,&VD);
}

// update Traffic secret and associated traffic key and IV
void deriveUpdatedKeys(crypto *context,octad *TS)
{
    int htype,sha,key;
    char info[16];
    octad INFO = {0,sizeof(info),info};
    char nts[TLS_MAX_HASH];
    octad NTS={0,sizeof(nts),nts};

// find cipher suite
    htype=SAL_hashType(context->suite);
    sha=SAL_hashLen(htype);

    key=SAL_aeadKeylen(context->suite); // depends on key length

    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"traffic upd");
    hkdfExpandLabel(htype,&NTS,sha,TS,&INFO,NULL);

    OCT_copy(TS,&NTS);

    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"key");
    hkdfExpandLabel(htype,&(context->K),key,TS,&INFO,NULL);

    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"iv");
    hkdfExpandLabel(htype,&(context->IV),12,TS,&INFO,NULL);
// reset record number
    context->record=0;
    context->active=true;
}

// Create a crypto context from an input raw Secret and an agreed cipher_suite 
void createCryptoContext(int cipher_suite,octad *TS,crypto *context)
{
    int key,htype=SAL_hashType(cipher_suite);

    key=SAL_aeadKeylen(cipher_suite);

    char info[8];
    octad INFO = {0,sizeof(info),info};

    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"key");
    hkdfExpandLabel(htype,&(context->K),key,TS,&INFO,NULL);

    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"iv");
    hkdfExpandLabel(htype,&(context->IV),12,TS,&INFO,NULL);

    context->active=true;
    context->suite=cipher_suite;
    context->record=0;
    context->taglen=SAL_aeadTaglen(cipher_suite);
}

void createSendCryptoContext(TLS_session *session,octad *TS)
{
    createCryptoContext(session->cipher_suite,TS,&session->K_send);
}

void createRecvCryptoContext(TLS_session *session,octad *TS)
{
    createCryptoContext(session->cipher_suite,TS,&session->K_recv);
}

// recover Pre-Shared-Key from Resumption Master Secret
void recoverPSK(TLS_session *session)
{
    char info[16];
    octad INFO = {0,sizeof(info),info};
    int htype=SAL_hashType(session->cipher_suite);
    int hlen=SAL_hashLen(htype);
    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"resumption");
    hkdfExpandLabel(htype,&session->T.PSK,hlen,&session->RMS, &INFO, &session->T.NONCE);
}

// Key Schedule code
// Get Early Secret ES and optional Binder Key (either External or Resumption)
void deriveEarlySecrets(int htype,octad *PSK,octad *ES,octad *BKE,octad *BKR)
{
    char emh[TLS_MAX_HASH];
    octad EMH = {0,sizeof(emh),emh};  
    char zk[TLS_MAX_HASH];              
    octad ZK = {0,sizeof(zk),zk}; 
    char info[16];
    octad INFO = {0,sizeof(info),info};

    int hlen=SAL_hashLen(htype);

    OCT_append_byte(&ZK,0,hlen);    // Zero key

    if (PSK==NULL)
        OCT_copy(&EMH,&ZK);            // if no PSK available use ZK
    else
        OCT_copy(&EMH,PSK);

    SAL_hkdfExtract(htype,ES,&ZK,&EMH);  // hash function, ES is output, ZK is salt and EMH is IKM

    SAL_hashNull(htype,&EMH);        // EMH = hash of ""

    if (BKE!=NULL)
    {  // External Binder Key
        OCT_kill(&INFO);
        OCT_append_string(&INFO,(char *)"ext binder");
        hkdfExpandLabel(htype,BKE,hlen,ES,&INFO,&EMH);
    }
    if (BKR!=NULL)
    { // Resumption Binder Key
        OCT_kill(&INFO);
        OCT_append_string(&INFO,(char *)"res binder");
        hkdfExpandLabel(htype,BKR,hlen,ES,&INFO,&EMH);
    }
}

// Get Later Secrets (Client Early Traffic Secret CETS and Early Exporter Master Secret EEMS) - requires partial transcript hash H
void deriveLaterSecrets(int htype,octad *ES,octad *H,octad *CETS,octad *EEMS)
{
    char info[16];
    octad INFO = {0,sizeof(info),info};
    int hlen=SAL_hashLen(htype);

    if (CETS!=NULL)
    {
        OCT_kill(&INFO);
        OCT_append_string(&INFO,(char *)"c e traffic");
        hkdfExpandLabel(htype,CETS,hlen,ES,&INFO,H);
    }
    if (EEMS!=NULL)
    {
        OCT_kill(&INFO);
        OCT_append_string(&INFO,(char *)"e exp master");
        hkdfExpandLabel(htype,EEMS,hlen,ES,&INFO,H);
    }
}

// get Client and Server Handshake secrets for encrypting rest of handshake, from Shared secret SS and early secret ES
void deriveHandshakeSecrets(TLS_session *session,octad *SS,octad *ES,octad *H)
{
    char ds[TLS_MAX_HASH];
    octad DS = {0,sizeof(ds),ds};       // Derived Secret
    char emh[TLS_MAX_HASH];
    octad EMH = {0,sizeof(emh),emh};    // Empty Hash
    char info[16];
    octad INFO = {0,sizeof(info),info};
    int htype=SAL_hashType(session->cipher_suite);
    int hlen=SAL_hashLen(htype);

    SAL_hashNull(htype,&EMH);            // hash of ""

    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"derived");
    hkdfExpandLabel(htype,&DS,hlen,ES,&INFO,&EMH);  

    SAL_hkdfExtract(htype,&session->HS,&DS,SS);

    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"c hs traffic");
    hkdfExpandLabel(htype,&session->CTS,hlen,&session->HS,&INFO,H);

    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"s hs traffic");
    hkdfExpandLabel(htype,&session->STS,hlen,&session->HS,&INFO,H);
}

// Extract Client and Server Application Traffic secrets from Transcript Hashes, Handshake secret 
// SFH - Server Finished Hash
// CFH - Client Finished Hash
void deriveApplicationSecrets(TLS_session *session,octad *SFH,octad *CFH,octad *EMS)
{
    char ds[TLS_MAX_HASH];
    octad DS = {0,sizeof(ds),ds};
    char ms[TLS_MAX_HASH];
    octad MS = {0,sizeof(ms),ms};
    char emh[TLS_MAX_HASH];
    octad EMH = {0,sizeof(emh),emh};
    char zk[TLS_MAX_HASH];                    // Zero Key
    octad ZK = {0,sizeof(zk),zk};
    char info[16];
    octad INFO = {0,sizeof(info),info};
    int htype=SAL_hashType(session->cipher_suite);
    int hlen=SAL_hashLen(htype);

    OCT_append_byte(&ZK,0,hlen);            // 00..00  
    SAL_hashNull(htype,&EMH);                // hash("")

    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"derived");
    hkdfExpandLabel(htype,&DS,hlen,&session->HS,&INFO,&EMH);   // Use handshake secret from above

    SAL_hkdfExtract(htype,&MS,&DS,&ZK);

    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"c ap traffic");
    hkdfExpandLabel(htype,&session->CTS,hlen,&MS,&INFO,SFH);

    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"s ap traffic");
    hkdfExpandLabel(htype,&session->STS,hlen,&MS,&INFO,SFH);

    if (EMS!=NULL)
    {
        OCT_kill(&INFO);
        OCT_append_string(&INFO,(char *)"exp master");
        hkdfExpandLabel(htype,EMS,hlen,&MS,&INFO,SFH);
    }

    OCT_kill(&INFO);
    OCT_append_string(&INFO,(char *)"res master");
    hkdfExpandLabel(htype,&session->RMS,hlen,&MS,&INFO,CFH);
}

// Create Client Cert Verify message, a digital signature using KEY on some TLS1.3 specific message+transcript hash
void createClientCertVerifier(int sigAlg,octad *H,octad *KEY,octad *CCVSIG)
{
    char ccv[100+TLS_MAX_HASH];
    octad CCV={0,sizeof(ccv),ccv};
// create TLS1.3 message to be signed
    OCT_append_byte(&CCV,32,64); // 64 spaces
    OCT_append_string(&CCV,(char *)"TLS 1.3, client CertificateVerify");  // 33 chars
    OCT_append_byte(&CCV,0,1);   // add 0 character
    OCT_append_octad(&CCV,H);    // add Transcript Hash 

    if (sigAlg==DILITHIUM2_P256)
    {
        octad FKEY={32,32,KEY->val};
        octad SKEY={KEY->len-32,KEY->len-32,&KEY->val[32]};
        SAL_tlsSignature(ECDSA_SECP256R1_SHA384,&FKEY,&CCV,CCVSIG);
        ecdsa_sig_encode(CCVSIG);  // ASN.1 encode it - it grows
        octad SSIG={0,TLS_MAX_SIGNATURE_SIZE-32,&CCVSIG->val[CCVSIG->len]};
        SAL_tlsSignature(DILITHIUM2,&SKEY,&CCV,&SSIG); // append PQ sig
        CCVSIG->len += SSIG.len;
        return;
    }

    SAL_tlsSignature(sigAlg,KEY,&CCV,CCVSIG);
// adjustment for ECDSA signatures
    if (sigAlg==ECDSA_SECP256R1_SHA256 || sigAlg==ECDSA_SECP384R1_SHA384)
    {
        ecdsa_sig_encode(CCVSIG);
    }
    return;
}

// check that SCVSIG is digital signature (using sigAlg algorithm) of some TLS1.3 specific message+transcript hash, 
// as verified by Server Certificate public key CERTPK
bool checkServerCertVerifier(int sigAlg,octad *SCVSIG,octad *H,octad *CERTPK)
{
// Server Certificate Verify
    char scv[100+TLS_MAX_HASH];
    octad SCV={0,sizeof(scv),scv};

// TLS1.3 message that was signed
    OCT_append_byte(&SCV,32,64); // 64 spaces
    OCT_append_string(&SCV,(char *)"TLS 1.3, server CertificateVerify");  // 33 chars
    OCT_append_byte(&SCV,0,1);   // add 0 character
    OCT_append_octad(&SCV,H);    // add Transcript Hash 

    if (sigAlg==DILITHIUM2_P256)
    {
        octad FPUB={65,65,CERTPK->val};
        octad SPUB={CERTPK->len-65,CERTPK->len-65,&CERTPK->val[65]};
        int len=SCVSIG->len;   // full length
        int index=ecdsa_sig_decode(SCVSIG); // ASN.1 decode it - it shrinks - return undecoded length
        if (index==0) return false;
        int mlen=SCVSIG->len;               // modified length
        octad FSIG={mlen,mlen,SCVSIG->val};
        octad SSIG={len-index,len-index,&SCVSIG->val[index]};
        return SAL_tlsSignatureVerify(ECDSA_SECP256R1_SHA384,&SCV,&FSIG,&FPUB) && SAL_tlsSignatureVerify(DILITHIUM2,&SCV,&SSIG,&SPUB);
    }

// Special case processing required here for ECDSA signatures -  SCVSIG is modified
    if (sigAlg==ECDSA_SECP256R1_SHA256 || sigAlg==ECDSA_SECP384R1_SHA384) {
        if (!ecdsa_sig_decode(SCVSIG)) return false;
    }
    log(IO_DEBUG,(char *)"Certificate Signature = ",NULL,0,SCVSIG);
    return SAL_tlsSignatureVerify(sigAlg,&SCV,SCVSIG,CERTPK);
}
