//
// Main TLS1.3 protocol
//

#include "tls_protocol.h"

// Initialise TLS 1.3 session state
TLS_session TLS13_start(Socket *sockptr,char *hostname)
{
    TLS_session state;
    state.sockptr=sockptr;                                  // pointer to socket
    strcpy(state.hostname,hostname);                        // server to connection with

    state.status=TLS13_DISCONNECTED;
    state.max_record=0;
    state.cipher_suite=0;//TLS_AES_128_GCM_SHA256;          // cipher suite
    initCryptoContext(&state.K_send);                       // Transmission key
    initCryptoContext(&state.K_recv);                       // Reception key

    state.HS={0,TLS_MAX_HASH,state.rms};                    // handshake secret
    state.RMS={0,TLS_MAX_HASH,state.rms};                   // Resumption Master secret
    state.STS={0,TLS_MAX_HASH,state.sts};                   // Server traffic secret
    state.CTS={0,TLS_MAX_HASH,state.cts};                   // Client traffic secret
    state.CTX={0,TLS_MAX_HASH,state.ctx};                   // Client traffic secret
#ifdef SHALLOW_STACK
    state.IBUFF= {0,TLS_MAX_IBUFF_SIZE,(char *)malloc(TLS_MAX_IBUFF_SIZE)};  // main input buffer
    state.OBUFF={0,TLS_MAX_OBUFF_SIZE,(char *)malloc(TLS_MAX_OBUFF_SIZE)};  // main input buffer
#else
    state.IBUFF={0,TLS_MAX_IBUFF_SIZE,state.ibuff};
    state.OBUFF={0,TLS_MAX_OBUFF_SIZE,state.obuff};
#endif
    state.ptr=0;
    state.favourite_group=0;                                // default key exchange group
    state.server_cert_type=X509_CERT;                       // Server Cert type (could be raw public key)
    state.client_cert_type=X509_CERT;                       // Client Cert type (could be raw public key)
    initTicketContext(&state.T);                            // Resumption ticket - may be added to session state
    state.expect_heartbeats=false;                          // not expecting heartbeats
    state.allowed_to_heartbeat=false;                       // not allowed to heartbeat
    state.heartbeat_req_in_flight=false;                    // timestamp on outstanding request in flight, else 0
    return state;
}

// build client's chosen set of extensions, and assert expectations of server responses
// The User may want to change the mix of optional extensions
// mode=0 - full handshake
// mode=1 - resumption handshake
// mode=2 = External PSK handshake
static void buildExtensions(TLS_session *session,octad *EXT,octad *PK,ee_status *expectations,int mode)
{
    int groups[TLS_MAX_SUPPORTED_GROUPS];
    int nsg=SAL_groups(groups);
    int sigAlgs[TLS_MAX_SUPPORTED_SIGS];
    int nsa=SAL_sigs(sigAlgs);
    int sigAlgsCert[TLS_MAX_SUPPORTED_SIGS];
    int nsac=SAL_sigCerts(sigAlgsCert);
    char alpn[20];
    octad ALPN={0,sizeof(alpn),alpn};         // ALPN
    int tlsVersion=TLS1_3;
    int pskMode=PSKWECDHE;
#ifdef TLS_APPLICATION_PROTOCOL
    OCT_append_string(&ALPN,(char *)TLS_APPLICATION_PROTOCOL);
#endif
    if (mode!=0)
    {  // resumption
        nsg=1;
        groups[0]=session->favourite_group; // Only allow the group already agreed
    }
    OCT_kill(EXT);
#ifdef ENABLE_HEARTBEATS
    addHeartbeat(EXT); // I can heartbeat if you let me
#endif

// addCertAuthorities(EXT);

    addServerNameExt(EXT,session->hostname); expectations->server_name=true;  // Server Name extension - acknowledgement is expected
    addSupportedGroupsExt(EXT,nsg,groups);
    addKeyShareExt(EXT,session->favourite_group,PK); // only sending one public key
#ifdef TLS_APPLICATION_PROTOCOL
    addALPNExt(EXT,&ALPN); expectations->alpn=true; // only supporting one application protocol
#endif
    addPSKModesExt(EXT,pskMode);
    addVersionExt(EXT,tlsVersion);
#ifdef MAX_RECORD
    addRSLExt(EXT,MAX_RECORD);               // demand a fragment size limit
#else
    if (mode!=2)
    {
        addMFLExt(EXT,TLS_MAX_FRAG);  expectations->max_frag_length=true; // ask for max fragment length - server may not agree - but no harm in asking
    }
#endif
    addPadding(EXT,SAL_randomByte()%16);  // add some random padding (because I can)

    if (mode==0) // full handshake
    { // need signature related extensions for full handshake
        addSigAlgsExt(EXT,nsa,sigAlgs);
        addSigAlgsCertExt(EXT,nsac,sigAlgsCert);
#ifdef PREFER_RAW_SERVER_PUBLIC_KEY
        addServerRawPublicKey(EXT);
#endif
#ifdef PREFER_RAW_CLIENT_PUBLIC_KEY
        addClientRawPublicKey(EXT);
#endif
    } 
    if (mode==2)
    { // // PSK, but client authentication may still be sought
#ifdef PREFER_RAW_CLIENT_PUBLIC_KEY
        addClientRawPublicKey(EXT);
#endif
    }
}

// Phase 1 - Exchange Client/Server "Hellos"
static int TLS13_exchange_hellos(TLS_session *session)
{
    ret rtn;
    int i,pskid;
    int kex,hashtype;
    int nsc,nsg;
    bool resumption_required=false;

    int ciphers[TLS_MAX_CIPHER_SUITES];
    nsc=SAL_ciphers(ciphers);  
    int groups[TLS_MAX_SUPPORTED_GROUPS];
    nsg=SAL_groups(groups);

#ifdef SHALLOW_STACK
    octad CSK = {0, TLS_MAX_KEX_SECRET_KEY_SIZE, (char *)malloc(TLS_MAX_KEX_SECRET_KEY_SIZE)};
    octad CPK = {0, TLS_MAX_KEX_PUB_KEY_SIZE, (char *)malloc(TLS_MAX_KEX_PUB_KEY_SIZE)}; 
    octad SPK = {0, TLS_MAX_KEX_CIPHERTEXT_SIZE, (char *)malloc(TLS_MAX_KEX_CIPHERTEXT_SIZE)};   
    octad EXT = {0, TLS_MAX_EXTENSIONS, (char *)malloc(TLS_MAX_EXTENSIONS)};
#else
    char csk[TLS_MAX_KEX_SECRET_KEY_SIZE];   // clients key exchange secret key
    octad CSK = {0, sizeof(csk), csk};
    char cpk[TLS_MAX_KEX_PUB_KEY_SIZE];      // Client key exchange Public Key (shared memory)
    octad CPK = {0, sizeof(cpk), cpk}; 
    char spk[TLS_MAX_KEX_CIPHERTEXT_SIZE];
    octad SPK = {0, sizeof(spk), spk};       // Server's key exchange Public Key/Ciphertext
    char ext[TLS_MAX_EXTENSIONS];
    octad EXT={0,sizeof(ext),ext};           // Extensions   
#endif
    char ss[TLS_MAX_SHARED_SECRET_SIZE];     // key exchange Shared Secret 
    octad SS = {0, sizeof(ss), ss};    
    char ch[TLS_MAX_HELLO];              // Client Hello
    octad CH = {0, sizeof(ch), ch};      
    char es[TLS_MAX_HASH];               // Early Secret
    octad ES = {0,sizeof(es),es};
    char hh[TLS_MAX_HASH];               
    octad HH={0,sizeof(hh),hh};          // Transcript hashes 
    char cook[TLS_MAX_COOKIE];
    octad COOK={0,sizeof(cook),cook};    // Cookie
    char crn[32];
    octad CRN = {0, sizeof(crn), crn};

    ee_status enc_ext_resp={false,false,false,false};  // encrypted extensions expectations
    ee_status enc_ext_expt={false,false,false,false};  // encrypted extensions responses

    log(IO_PROTOCOL,(char *)"Attempting Full Handshake\n",NULL,0,NULL);

    session->favourite_group=groups[0]; // only sending one key share - so choose first in our list
//
// Generate key pair in favourite group
//
    SAL_generateKeyPair(session->favourite_group,&CSK,&CPK);   
    log(IO_DEBUG,(char *)"Private key= ",NULL,0,&CSK);
    log(IO_DEBUG,(char *)"Client Public key= ",NULL,0,&CPK);

// Client Hello
    SAL_randomOctad(32,&CRN);

// First build our preferred mix of client Hello extensions, based on our capabililities
    buildExtensions(session,&EXT,&CPK,&enc_ext_expt,0);

// create and send Client Hello octad
    sendClientHello(session,TLS1_0,&CH,&CRN,false,&EXT,0,false,true);  
//
//
//   ----------------------------------------------------------> client Hello
//
//    
    log(IO_DEBUG,(char *)"Client Hello sent\n",NULL,0,NULL);

// Process Server Hello response
    rtn=getServerHello(session,kex,&COOK,&SPK,pskid);
//
//
//  <--------------------------------- server Hello (or helloRetryRequest?)
//
//
    if (badResponse(session,rtn)) 
    {
#ifdef SHALLOW_STACK
        free(CSK.val); free(CPK.val); free(SPK.val); free(EXT.val);
#endif
        return TLS_FAILURE;
    }
// Find cipher-suite chosen by Server
    hashtype=0;
    for (i=0;i<nsc;i++)
    {
        if (session->cipher_suite==ciphers[i])
            hashtype=SAL_hashType(session->cipher_suite);
    }
    if (SAL_hashLen(hashtype)==0)
    {
        sendAlert(session,ILLEGAL_PARAMETER);
        logCipherSuite(session->cipher_suite);
        log(IO_DEBUG,(char *)"Cipher_suite not valid\n",NULL,0,NULL);
        log(IO_PROTOCOL,(char *)"Full Handshake failed\n",NULL,0,NULL);
#ifdef SHALLOW_STACK
        free(CSK.val); free(CPK.val); free(SPK.val);  free(EXT.val);
#endif
        return TLS_FAILURE;
    }
    logCipherSuite(session->cipher_suite);
    deriveEarlySecrets(hashtype,NULL,&ES,NULL,NULL);   // Early Secret
    log(IO_DEBUG,(char *)"Early Secret= ",NULL,0,&ES);

// Initialise Transcript Hash
// For Transcript hash we must use cipher-suite hash function
    initTranscriptHash(session);
    if (rtn.val==HANDSHAKE_RETRY)  // Was serverHello an helloRetryRequest?
    {
        log(IO_DEBUG,(char *)"Server HelloRetryRequest= ",NULL,0,&session->IBUFF);
        runningSyntheticHash(session,&CH,&EXT); // RFC 8446 section 4.4.1
        runningHashIOrewind(session);      // Hash of helloRetryRequest

        bool supported=false;
        for (int i=0;i<nsg;i++)
            if (kex==groups[i]) supported=true; 

        if (!supported || kex==session->favourite_group)  // kex is alternate group suggested by server
        { // its not supported or its the same one I originally chose !?
            sendAlert(session,ILLEGAL_PARAMETER);
            log(IO_DEBUG,(char *)"Group not supported, or no change as result of HRR\n",NULL,0,NULL);   
            log(IO_PROTOCOL,(char *)"Full Handshake failed\n",NULL,0,NULL);
#ifdef SHALLOW_STACK
            free(CSK.val); free(CPK.val); free(SPK.val);  free(EXT.val);
#endif
            return TLS_FAILURE;
        }

// Repair clientHello by supplying public key of Server's preferred key exchange algorithm
// build new client Hello extensions

// generate new key pair in new server selected group 
        session->favourite_group=kex;  // OK, lets try the alternate
        SAL_generateKeyPair(session->favourite_group,&CSK,&CPK); 
        buildExtensions(session,&EXT,&CPK,&enc_ext_expt,0);

        if (COOK.len!=0)
            addCookieExt(&EXT,&COOK);   // there was a cookie in the HRR ... so send it back in an extension
        sendCCCS(session);  // send Client Cipher Change

// create and send new Client Hello octad
        sendClientHello(session,TLS1_2,&CH,&CRN,false,&EXT,0,true,true);
//
//
//  ---------------------------------------------------> Resend Client Hello
//
//
        log(IO_DEBUG,(char *)"Client Hello re-sent\n",NULL,0,NULL);

        int skex; // Server Key Exchange Group - should be same as kex
        rtn=getServerHello(session,skex,&COOK,&SPK,pskid);
        
        if (badResponse(session,rtn)) 
        {
#ifdef SHALLOW_STACK
            free(CSK.val); free(CPK.val); free(SPK.val);  free(EXT.val);
#endif
            return TLS_FAILURE;
        }

        if (rtn.val==HANDSHAKE_RETRY)
        { // only one retry allowed
            log(IO_DEBUG,(char *)"A second Handshake Retry Request?\n",NULL,0,NULL); 
            sendAlert(session,UNEXPECTED_MESSAGE);
            log(IO_PROTOCOL,(char *)"Full Handshake failed\n",NULL,0,NULL);
#ifdef SHALLOW_STACK
            free(CSK.val); free(CPK.val); free(SPK.val);  free(EXT.val);
#endif
            return TLS_FAILURE;
        }

        if (kex!=skex)
        {
            log(IO_DEBUG,(char *)"Server came back with wrong group\n",NULL,0,NULL); 
            sendAlert(session,ILLEGAL_PARAMETER);
            log(IO_PROTOCOL,(char *)"Full Handshake failed\n",NULL,0,NULL);
#ifdef SHALLOW_STACK
            free(CSK.val); free(CPK.val); free(SPK.val);  free(EXT.val);
#endif
            return TLS_FAILURE;
        }
//
//
//  <---------------------------------------------------------- server Hello
//
//
        resumption_required=true;
    }
    log(IO_DEBUG,(char *)"Server Hello= ",NULL,0,&session->IBUFF); 
    logServerHello(session->cipher_suite,pskid,&SPK,&COOK);
    logKeyExchange(kex);
// Hash Transcript of the Hellos 
    runningHash(session,&CH);
    runningHash(session,&EXT);
    runningHashIOrewind(session);
    transcriptHash(session,&HH);        // HH = hash of clientHello+serverHello

// Generate Shared secret SS from Client Secret Key and Server's Public Key
    bool nonzero=SAL_generateSharedSecret(kex,&CSK,&SPK,&SS);
    if (!nonzero)
    { // all zero shared secret??
        sendAlert(session,ILLEGAL_PARAMETER);
        TLS13_clean(session);
#ifdef SHALLOW_STACK
        free(CSK.val); free(CPK.val); free(SPK.val);  free(EXT.val);
#endif
        return TLS_FAILURE;
    }
    log(IO_DEBUG,(char *)"Shared Secret= ",NULL,0,&SS);

// Extract Handshake secret, Client and Server Handshake Traffic secrets, Client and Server Handshake keys and IVs from Transcript Hash and Shared secret

    deriveHandshakeSecrets(session,&SS,&ES,&HH);

    createSendCryptoContext(session,&session->CTS);
    createRecvCryptoContext(session,&session->STS);

    log(IO_DEBUG,(char *)"Handshake Secret= ",NULL,0,&session->HS);
    log(IO_DEBUG,(char *)"Client handshake traffic secret= ",NULL,0,&session->CTS);
    log(IO_DEBUG,(char *)"Client handshake key= ",NULL,0,&(session->K_send.K));
    log(IO_DEBUG,(char *)"Client handshake iv= ",NULL,0,&(session->K_send.IV));
    log(IO_DEBUG,(char *)"Server handshake traffic secret= ",NULL,0,&session->STS);
    log(IO_DEBUG,(char *)"Server handshake key= ",NULL,0,&(session->K_recv.K));
    log(IO_DEBUG,(char *)"Server handshake iv= ",NULL,0,&(session->K_recv.IV));
#ifdef SHALLOW_STACK
    free(CSK.val); free(CPK.val); free(SPK.val);  free(EXT.val);
#endif
// 1. get encrypted extensions
    rtn=getServerEncryptedExtensions(session,&enc_ext_expt,&enc_ext_resp);   
//
//
//  <------------------------------------------------- {Encrypted Extensions}
//
//
    if (badResponse(session,rtn)) 
        return TLS_FAILURE;
    
    logEncExt(&enc_ext_expt,&enc_ext_resp);
    log(IO_DEBUG,(char *)"Encrypted Extensions Processed\n",NULL,0,NULL);
    if (resumption_required) return TLS_RESUMPTION_REQUIRED;
    return TLS_SUCCESS;
}

// Phase 2 - check that the server is trusted
static int TLS13_server_trust(TLS_session *session)
{
    ret rtn;
    int hashtype;

#ifdef SHALLOW_STACK
    octad SERVER_PK = {0,TLS_MAX_SIG_PUB_KEY_SIZE,(char *)malloc(TLS_MAX_SIG_PUB_KEY_SIZE)};  // Server's cert sig public key
    octad SCVSIG={0,TLS_MAX_SIGNATURE_SIZE,(char *)malloc(TLS_MAX_SIGNATURE_SIZE)};           // Server's digital signature on transcript
#else
    char server_pk[TLS_MAX_SIG_PUB_KEY_SIZE];
    octad SERVER_PK = {0,sizeof(server_pk),server_pk}; // Server's cert sig public key
    char scvsig[TLS_MAX_SIGNATURE_SIZE];
    octad SCVSIG={0,sizeof(scvsig),scvsig};           // Server's digital signature on transcript
#endif
    char hh[TLS_MAX_HASH];               
    octad HH={0,sizeof(hh),hh};          // Transcript hashes
    char fh[TLS_MAX_HASH];
    octad FH={0,sizeof(fh),fh};       

    char fin[TLS_MAX_HASH];
    octad FIN={0,sizeof(fin),fin};                    // Server's finish message

    hashtype=SAL_hashType(session->cipher_suite);

// Client now receives certificate chain and verifier from Server. Need to parse these out, check CA signature on the cert
// (maybe its self-signed), extract public key from cert, and use this public key to check server's signature 
// on the "verifier". Note Certificate signature might use old methods, but server will use PSS padding for its signature (or ECC).
    rtn=getCheckServerCertificateChain(session,&SERVER_PK,&SCVSIG);  // note SCVSIG is used here as workspace
    if (session->server_cert_type==RAW_PUBLIC_KEY) {
        log(IO_PROTOCOL,(char *)"WARNING - server is authenticating with raw public key\n",NULL,0,NULL);
    }
//
//
//  <---------------------------------------------------------- {Certificate}
//
//
    if (badResponse(session,rtn))
    {    
#ifdef SHALLOW_STACK
        free(SERVER_PK.val); free (SCVSIG.val);
#endif
        return TLS_FAILURE;
    }
    transcriptHash(session,&HH); // HH = hash of clientHello+serverHello+encryptedExtensions+CertChain
    log(IO_DEBUG,(char *)"Certificate Chain is valid\n",NULL,0,NULL);
    log(IO_DEBUG,(char *)"Transcript Hash (CH+SH+EE+CT) = ",NULL,0,&HH); 

// 3. get verifier signature
    int sigalg;
    rtn=getServerCertVerify(session,&SCVSIG,sigalg);
//
//
//  <---------------------------------------------------- {Certificate Verify}
//
//
    if (badResponse(session,rtn)) 
    {
#ifdef SHALLOW_STACK
        free(SERVER_PK.val); free (SCVSIG.val);
#endif
        return TLS_FAILURE;
    }
    transcriptHash(session,&FH); // hash of clientHello+serverHello+encryptedExtensions+CertChain+serverCertVerify
    log(IO_DEBUG,(char *)"Transcript Hash (CH+SH+EE+SCT+SCV) = ",NULL,0,&FH);
    log(IO_DEBUG,(char *)"Server Transcript Signature= ",NULL,0,&SCVSIG);

    logSigAlg(sigalg);
    if (!checkServerCertVerifier(sigalg,&SCVSIG,&HH,&SERVER_PK))
    {
        sendAlert(session,DECRYPT_ERROR);
        log(IO_DEBUG,(char *)"Server Cert Verification failed\n",NULL,0,NULL);
        log(IO_PROTOCOL,(char *)"Full Handshake failed\n",NULL,0,NULL);
#ifdef SHALLOW_STACK
        free(SERVER_PK.val); free (SCVSIG.val);
#endif
        return TLS_FAILURE;
    }
    log(IO_PROTOCOL,(char *)"Server Cert Verification OK - ",session->hostname,0,NULL);
#ifdef SHALLOW_STACK
    free(SERVER_PK.val); free (SCVSIG.val);
#endif
// 4. get Server Finished
    rtn=getServerFinished(session,&FIN);
//
//
//  <------------------------------------------------------ {Server Finished}
//
//
    if (badResponse(session,rtn))
        return TLS_FAILURE;
    
    if (!checkVeriferData(hashtype,&FIN,&session->STS,&FH))
    {
        sendAlert(session,DECRYPT_ERROR);
        log(IO_DEBUG,(char *)"Server Data is NOT verified\n",NULL,0,NULL);
        log(IO_DEBUG,(char *)"Full Handshake failed\n",NULL,0,NULL);
        return TLS_FAILURE;
    }
    log(IO_DEBUG,(char *)"\nServer Data is verified\n",NULL,0,NULL);

    return TLS_SUCCESS;
}

// Phase 3 - client supplies trust to server, given servers list of acceptable signature types
#if CLIENT_CERT != NOCERT
static void TLS13_client_trust(TLS_session *session)
{
#ifdef SHALLOW_STACK
    octad CLIENT_KEY={0,TLS_MAX_SIG_SECRET_KEY_SIZE,(char *)malloc(TLS_MAX_SIG_SECRET_KEY_SIZE)};   // Client secret key
    octad CLIENT_CERTCHAIN={0,TLS_MAX_CLIENT_CHAIN_SIZE,(char *)malloc(TLS_MAX_CLIENT_CHAIN_SIZE)};   // Client certificate chain
    octad CCVSIG={0,TLS_MAX_SIGNATURE_SIZE,(char *)malloc(TLS_MAX_SIGNATURE_SIZE)}; 
#else 
    char client_key[TLS_MAX_SIG_SECRET_KEY_SIZE];           
    octad CLIENT_KEY={0,sizeof(client_key),client_key};   // Client secret key
    char client_certchain[TLS_MAX_CLIENT_CHAIN_SIZE];           
    octad CLIENT_CERTCHAIN={0,sizeof(client_certchain),client_certchain};   // Client certificate chain
    char ccvsig[TLS_MAX_SIGNATURE_SIZE];
    octad CCVSIG={0,sizeof(ccvsig),ccvsig};           // Client's digital signature on transcript
#endif
    char th[TLS_MAX_HASH];
    octad TH={0,sizeof(th),th};  // Transcript hash

    int kind=getClientPrivateKeyandCertChain(&CLIENT_KEY,session->client_cert_type,&CLIENT_CERTCHAIN);

    if (kind!=0)
    { // Yes, I can do that kind of signature
        log(IO_PROTOCOL,(char *)"Client is authenticating\n",NULL,0,NULL);
        sendClientCertificateChain(session,&CLIENT_CERTCHAIN);
//
//
//  {client Certificate} ---------------------------------------------------->
//
//
        transcriptHash(session,&TH);
        createClientCertVerifier(kind,&TH,&CLIENT_KEY,&CCVSIG);      
        sendClientCertVerify(session,kind,&CCVSIG);
//
//
//  {Certificate Verify} ---------------------------------------------------->
//
//
    } else { // No, I can't - send a null cert, and no verifier
        sendClientCertificateChain(session,NULL);
    }
#ifdef SHALLOW_STACK
    free(CLIENT_KEY.val); free(CLIENT_CERTCHAIN.val); free(CCVSIG.val);
#endif
}
#endif

// TLS1.3 full handshake - connect to server
static int TLS13_full(TLS_session *session)
{
    ret rtn;
    int hashtype;
    bool resumption_required=false;
    bool gotacertrequest=false;
    bool have_suitable_cert=false;

    char hh[TLS_MAX_HASH];               
    octad HH={0,sizeof(hh),hh};          // Transcript hashes  
    char th[TLS_MAX_HASH];
    octad TH={0,sizeof(th),th};  
    char chf[TLS_MAX_HASH];                           
    octad CHF={0,sizeof(chf),chf};       // client verify

// Exchange Hellos
    int rval=TLS13_exchange_hellos(session);
    if (rval==TLS_FAILURE)
    {
        TLS13_clean(session);
        return TLS_FAILURE;
    }
    if (rval==TLS_RESUMPTION_REQUIRED)
        resumption_required=true;
        
// get certificate request (maybe..) and certificate chain, check it, get Server public key

    hashtype=SAL_hashType(session->cipher_suite);
    rtn=seeWhatsNext(session);  // get next message type
    if (badResponse(session,rtn)) 
    {
        TLS13_clean(session);
        return TLS_FAILURE;
    }

    if (rtn.val==CERT_REQUEST)
    { // optional certificate request received
        gotacertrequest=true;
        rtn=getCertificateRequest(session,false);
//
//
//  <---------------------------------------------------- {Certificate Request}
//
//
        if (badResponse(session,rtn))
        {
            TLS13_clean(session);
            return TLS_FAILURE;
        }
        if (rtn.val==CERT_REQUEST) {
            have_suitable_cert=true;
        }
        log(IO_PROTOCOL,(char *)"Certificate Request received\n",NULL,0,NULL);
    }

    rval=TLS13_server_trust(session);
    if (rval==TLS_FAILURE)
    {
        TLS13_clean(session);
        return TLS_FAILURE;
    }
    sendCCCS(session);  // send Client Cipher Change
    transcriptHash(session,&HH); // hash of clientHello+serverHello+encryptedExtensions+CertChain+serverCertVerify+serverFinish

    log(IO_DEBUG,(char *)"Transcript Hash (CH+SH+EE+SCT+SCV+SF) YYY = ",NULL,0,&HH);

// Now its the clients turn to respond
// Send Certificate (if it was asked for, and if I have one) & Certificate Verify.
    OCT_kill(&session->IBUFF);
    session->ptr=0;

    if (gotacertrequest)
    {
        if (have_suitable_cert) {
            TLS13_client_trust(session);
        } else {
            sendClientCertificateChain(session,NULL);
        }
    } 
    transcriptHash(session,&TH);

// HH is server finished hash
// TH is client finished hash
// both are needed

    log(IO_DEBUG,(char *)"Transcript Hash (CH+SH+EE+SCT+SCV+SF+[CCT+CSV])  XXXX = ",NULL,0,&TH);

// create client verify data
// .... and send it to Server
    deriveVeriferData(hashtype,&CHF,&session->CTS,&TH);  
    sendClientFinish(session,&CHF);  
//
//
//  {client Finished} ----------------------------------------------------->
//
//
    log(IO_DEBUG,(char *)"Client Verify Data= ",NULL,0,&CHF); 
    transcriptHash(session,&TH); // hash of clientHello+serverHello+encryptedExtensions+CertChain+serverCertVerify+serverFinish(+clientCertChain+clientCertVerify)+clientFinish
    log(IO_DEBUG,(char *)"Transcript Hash (CH+SH+EE+SCT+SCV+SF+[CCT+CSV]+CF) = ",NULL,0,&TH);

// calculate traffic and application keys from handshake secret and transcript hashes
    deriveApplicationSecrets(session,&HH,&TH,NULL);

    createSendCryptoContext(session,&session->CTS);
    createRecvCryptoContext(session,&session->STS);

    log(IO_DEBUG,(char *)"Client application traffic secret= ",NULL,0,&session->CTS);
    log(IO_DEBUG,(char *)"Server application traffic secret= ",NULL,0,&session->STS);
    log(IO_PROTOCOL,(char *)"FULL Handshake succeeded\n",NULL,0,NULL);
    if (resumption_required) log(IO_PROTOCOL,(char *)"... after handshake resumption\n",NULL,0,NULL);

    OCT_kill(&session->IBUFF);  // clean up IO buffer

    if (resumption_required) return TLS_RESUMPTION_REQUIRED;
    return TLS_SUCCESS;
}

// TLS1.3 fast resumption handshake (0RTT and 1RTT)
// EARLY - First message from Client to Server (should ideally be sent as early data!)
static int TLS13_resume(TLS_session *session,octad *EARLY)
{
    int hashtype,kex,pskid;
    ret rtn;

#ifdef SHALLOW_STACK
    octad CSK = {0, TLS_MAX_KEX_SECRET_KEY_SIZE, (char *)malloc(TLS_MAX_KEX_SECRET_KEY_SIZE)};
    octad CPK = {0, TLS_MAX_KEX_PUB_KEY_SIZE, (char *)malloc(TLS_MAX_KEX_PUB_KEY_SIZE)}; 
    octad SPK = {0, TLS_MAX_KEX_CIPHERTEXT_SIZE, (char *)malloc(TLS_MAX_KEX_CIPHERTEXT_SIZE)};   
    octad EXT = {0, TLS_MAX_EXTENSIONS, (char *)malloc(TLS_MAX_EXTENSIONS)};
#else
    char csk[TLS_MAX_KEX_SECRET_KEY_SIZE];   
    octad CSK = {0, sizeof(csk), csk};   // clients key exchange secret key
    char cpk[TLS_MAX_KEX_PUB_KEY_SIZE];
    octad CPK = {0, sizeof(cpk), cpk};   // Client's key exchange Public Key
    char spk[TLS_MAX_KEX_CIPHERTEXT_SIZE];
    octad SPK = {0, sizeof(spk), spk};   // Server's key exchange Public Key/Ciphertext
    char ext[TLS_MAX_EXTENSIONS];
    octad EXT={0,sizeof(ext),ext};       // Extensions  
#endif
    char es[TLS_MAX_HASH];               // Early Secret
    octad ES = {0,sizeof(es),es};
    char ss[TLS_MAX_SHARED_SECRET_SIZE];
    octad SS = {0, sizeof(ss), ss};      // Shared Secret
    char ch[TLS_MAX_HELLO];                 // Client Hello
    octad CH = {0, sizeof(ch), ch};

    char hh[TLS_MAX_HASH];               
    octad HH={0,sizeof(hh),hh};          // Transcript hashes
    char fh[TLS_MAX_HASH];
    octad FH={0,sizeof(fh),fh};       
    char th[TLS_MAX_HASH];
    octad TH={0,sizeof(th),th};  
    char fin[TLS_MAX_HASH];
    octad FIN={0,sizeof(fin),fin};      // Server's finish message
    char chf[TLS_MAX_HASH];                           
    octad CHF={0,sizeof(chf),chf};      // client verify
    char cets[TLS_MAX_HASH];           
    octad CETS={0,sizeof(cets),cets};   // Early traffic secret
    char cook[TLS_MAX_COOKIE];
    octad COOK={0,sizeof(cook),cook};   // Cookie
    char bnd[TLS_MAX_HASH];
    octad BND={0,sizeof(bnd),bnd};
    char psk[TLS_MAX_HASH];
    octad PSK={0,sizeof(psk),psk};      // Pre-shared key
    char bk[TLS_MAX_HASH];
    octad BK={0,sizeof(bk),bk};         // Binder key
    char crn[32];
    octad CRN = {0, sizeof(crn), crn};

    unsign32 time_ticket_received,time_ticket_used;
    int origin;
    unsign32 age,age_obfuscator=0;
    unsign32 max_early_data=0;
#ifdef TRY_EARLY_DATA
    bool have_early_data=true;       // Hope to send client message as early data
#else
    bool have_early_data=false;
#endif
    bool external_psk=false;
    ee_status enc_ext_resp={false,false,false,false};  // encrypted extensions responses 
    ee_status enc_ext_expt={false,false,false,false};  // encrypted extensions expectations

// Extract Ticket parameters
    age_obfuscator=session->T.age_obfuscator;
    max_early_data=session->T.max_early_data;
    OCT_copy(&PSK,&session->T.PSK);
    time_ticket_received=session->T.birth;
    session->cipher_suite=session->T.cipher_suite;
    session->favourite_group=session->T.favourite_group;
    origin=session->T.origin;

    log(IO_PROTOCOL,(char *)"Attempting Resumption Handshake\n",NULL,0,NULL);
    logTicket(&session->T); // lifetime,age_obfuscator,max_early_data,&NONCE,&ETICK);

    if (max_early_data==0 || EARLY==NULL)
        have_early_data=false;      // early data not allowed - or I don't have any

// Generate Early secret and Binder Key from PSK

    hashtype=SAL_hashType(session->cipher_suite);
    initTranscriptHash(session);

    if (origin==TLS_EXTERNAL_PSK)
    { // its an external PSK
        external_psk=true;
        deriveEarlySecrets(hashtype,&PSK,&ES,&BK,NULL);
    } else {
        external_psk=false;
        deriveEarlySecrets(hashtype,&PSK,&ES,NULL,&BK);   // compute early secret and Binder Key from PSK
    }
    //log(IO_DEBUG,(char *)"PSK= ",NULL,0,&PSK); 
    log(IO_DEBUG,(char *)"Binder Key= ",NULL,0,&BK); 
    log(IO_DEBUG,(char *)"Early Secret= ",NULL,0,&ES);

// Generate key pair in favourite group - use same favourite group that worked before for this server - so should be no HRR
    SAL_generateKeyPair(session->favourite_group,&CSK,&CPK);
    log(IO_DEBUG,(char *)"Private key= ",NULL,0,&CSK);  
    log(IO_DEBUG,(char *)"Client Public key= ",NULL,0,&CPK);  

// Client Hello
    SAL_randomOctad(32,&CRN);
// First build standard client Hello extensions

    int resmode=1;
    if (origin==TLS_EXTERNAL_PSK)
        resmode=2;
    buildExtensions(session,&EXT,&CPK,&enc_ext_expt,resmode);    
    
    if (have_early_data) {
        addEarlyDataExt(&EXT); enc_ext_expt.early_data=true;   // try sending client message as early data if allowed
    }
#ifdef POST_HS_AUTH
    addPostHSAuth(&EXT);  // willing to do post handshake authentication if necessary
#endif
    age=0;
    if (!external_psk)
    { // Its NOT an external pre-shared key
        time_ticket_used=(unsign32)millis();
        age=time_ticket_used-time_ticket_received; // age of ticket in milliseconds - problem for some sites which work for age=0 ??
        log(IO_DEBUG,(char *)"Ticket age= ",(char *)"%x",age,NULL);
        age+=age_obfuscator;
        log(IO_DEBUG,(char *)"obfuscated age = ",(char *)"%x",age,NULL);
    }

    int extra=addPreSharedKeyExt(&EXT,age,&session->T.TICK,SAL_hashLen(hashtype)); // must be last extension..

// create and send Client Hello octad
    sendClientHello(session,TLS1_2,&CH,&CRN,true,&EXT,extra,false,false);  // don't transmit yet - wait for binders
//
//
//   ----------------------------------------------------------> client Hello
//
//
    runningHash(session,&CH); 
    runningHash(session,&EXT);
    transcriptHash(session,&HH);            // HH = hash of Truncated clientHello
    log(IO_DEBUG,(char *)"Hash of Truncated client Hello",NULL,0,&HH);
    deriveVeriferData(hashtype,&BND,&BK,&HH);
    sendBinder(session,&BND);               // Send Binders
    log(IO_DEBUG,(char *)"Client Hello + Binder sent\n",NULL,0,NULL);
    log(IO_DEBUG,(char *)"Binder= ",NULL,0,&BND);
  
    transcriptHash(session,&HH);            // HH = hash of full clientHello
    log(IO_DEBUG,(char *)"Hash of Completed client Hello",NULL,0,&HH);

    if (have_early_data)
        sendCCCS(session);

    deriveLaterSecrets(hashtype,&ES,&HH,&CETS,NULL);   // Get Client Early Traffic Secret from transcript hash and ES
    log(IO_DEBUG,(char *)"Client Early Traffic Secret= ",NULL,0,&CETS); 
    createSendCryptoContext(session,&CETS);
// if its allowed, send client message as (encrypted!) early data
    if (have_early_data)
    {
        log(IO_APPLICATION,(char *)"Sending some early data\n",NULL,0,NULL);
        sendClientMessage(session,APPLICATION,TLS1_2,EARLY,NULL,true);
//
//
//   ----------------------------------------------------------> (Early Data)
//
//
    } 

// Process Server Hello
    rtn=getServerHello(session,kex,&COOK,&SPK,pskid);
    if (badResponse(session,rtn)) 
    {
        TLS13_clean(session);
#ifdef SHALLOW_STACK
        free(CSK.val); free(CPK.val); free(SPK.val); free(EXT.val);
#endif
        return TLS_FAILURE;
    }
//
//
//  <---------------------------------------------------------- server Hello
//
//
    runningHashIOrewind(session); // Hashing Server Hello
    transcriptHash(session,&HH);       // HH = hash of clientHello+serverHello

    if (pskid<0)
    { // Ticket rejected by Server (as out of date??)
        log(IO_PROTOCOL,(char *)"Ticket rejected by server\n",NULL,0,NULL);
        log(IO_PROTOCOL,(char *)"Resumption Handshake failed\n",NULL,0,NULL);
        TLS13_clean(session);
#ifdef SHALLOW_STACK
        free(CSK.val); free(CPK.val); free(SPK.val);  free(EXT.val);
#endif
        return TLS_FAILURE; 
    }

    if (pskid>0)
    { // pskid out-of-range (only one allowed)
        sendAlert(session,ILLEGAL_PARAMETER);
        log(IO_PROTOCOL,(char *)"Resumption Handshake failed\n",NULL,0,NULL);
        TLS13_clean(session);
#ifdef SHALLOW_STACK
        free(CSK.val); free(CPK.val); free(SPK.val);  free(EXT.val);
#endif
        return TLS_FAILURE;
    }

    logServerHello(session->cipher_suite,pskid,&SPK,&COOK);
    logKeyExchange(kex);

    if (rtn.val==HANDSHAKE_RETRY || kex!=session->favourite_group)
    { // should not happen
        sendAlert(session,UNEXPECTED_MESSAGE);
        log(IO_DEBUG,(char *)"No change possible as result of HRR\n",NULL,0,NULL); 
        log(IO_PROTOCOL,(char *)"Resumption Handshake failed\n",NULL,0,NULL);
        TLS13_clean(session);
#ifdef SHALLOW_STACK
        free(CSK.val); free(CPK.val); free(SPK.val);  free(EXT.val);
#endif
        return TLS_FAILURE;
    }
    //log(IO_DEBUG,(char *)"serverHello= ",NULL,0,&session->IBUFF); 

// Generate Shared secret SS from Client Secret Key and Server's Public Key
    bool nonzero=SAL_generateSharedSecret(kex,&CSK,&SPK,&SS);
    if (!nonzero)
    { // all zero shared secret??
        sendAlert(session,ILLEGAL_PARAMETER);
        TLS13_clean(session);
#ifdef SHALLOW_STACK
        free(CSK.val); free(CPK.val); free(SPK.val);  free(EXT.val);
#endif
        return TLS_FAILURE;
    }
    log(IO_DEBUG,(char *)"Shared Secret= ",NULL,0,&SS);

    deriveHandshakeSecrets(session,&SS,&ES,&HH); 
    createRecvCryptoContext(session,&session->STS);

    log(IO_DEBUG,(char *)"Handshake Secret= ",NULL,0,&session->HS);
    log(IO_DEBUG,(char *)"Client handshake traffic secret= ",NULL,0,&session->CTS);
    log(IO_DEBUG,(char *)"Server handshake traffic secret= ",NULL,0,&session->STS);

#ifdef SHALLOW_STACK
    free(CSK.val); free(CPK.val); free(SPK.val);  free(EXT.val);
#endif

// 1. get encrypted extensions
    rtn=getServerEncryptedExtensions(session,&enc_ext_expt,&enc_ext_resp);
//
//
//  <------------------------------------------------- {Encrypted Extensions}
//
//
    if (badResponse(session,rtn)) 
    {
        TLS13_clean(session);
        return TLS_FAILURE;
    }
    logEncExt(&enc_ext_expt,&enc_ext_resp);
    transcriptHash(session,&FH); // hash of clientHello+serverHello+encryptedExtension
    log(IO_DEBUG,(char *)"Transcript Hash (CH+SH+EE) = ",NULL,0,&FH); 

// 2. get server finish
    rtn=getServerFinished(session,&FIN);   // Finished
//
//
//  <------------------------------------------------------ {Server Finished}
//
//
    if (badResponse(session,rtn)) 
    {
        TLS13_clean(session);
        return TLS_FAILURE;
    }

// Now indicate End of Early Data, encrypted with 0-RTT keys
    transcriptHash(session,&HH); // hash of clientHello+serverHello+encryptedExtension+serverFinish
    if (enc_ext_resp.early_data)
    {
        sendEndOfEarlyData(session);     // Should only be sent if server has accepted Early data - see encrypted extensions!
        log(IO_DEBUG,(char *)"Send End of Early Data \n",NULL,0,NULL);
    }
    transcriptHash(session,&TH); // hash of clientHello+serverHello+encryptedExtension+serverFinish+EndOfEarlyData
    log(IO_DEBUG,(char *)"Transcript Hash (CH+SH+EE+SF+ED) = ",NULL,0,&TH); 

// Switch to handshake keys
    createSendCryptoContext(session,&session->CTS);
    if (!checkVeriferData(hashtype,&FIN,&session->STS,&FH))
    {
        sendAlert(session,DECRYPT_ERROR);
        log(IO_DEBUG,(char *)"Server Data is NOT verified\n",NULL,0,NULL);
        log(IO_PROTOCOL,(char *)"Resumption Handshake failed\n",NULL,0,NULL);
        TLS13_clean(session);
        return TLS_FAILURE;
    }

// create client verify data and send it to Server
    deriveVeriferData(hashtype,&CHF,&session->CTS,&TH);  
    sendClientFinish(session,&CHF);  
//
//
//  {client Finished} ----------------------------------------------------->
//
//
    log(IO_DEBUG,(char *)"Server Data is verified\n",NULL,0,NULL);
    log(IO_DEBUG,(char *)"Client Verify Data= ",NULL,0,&CHF); 

    transcriptHash(session,&FH); // hash of clientHello+serverHello+encryptedExtension+serverFinish+EndOfEarlyData+clientFinish

// calculate traffic and application keys from handshake secret and transcript hashes, and store in session
    deriveApplicationSecrets(session,&HH,&FH,NULL);  
    createSendCryptoContext(session,&session->CTS);
    createRecvCryptoContext(session,&session->STS);

    log(IO_DEBUG,(char *)"Client application traffic secret= ",NULL,0,&session->CTS);
    log(IO_DEBUG,(char *)"Server application traffic secret= ",NULL,0,&session->STS);
    log(IO_PROTOCOL,(char *)"RESUMPTION Handshake succeeded\n",NULL,0,NULL);

    OCT_kill(&session->IBUFF);  // clean up IO buffer

    if (enc_ext_resp.early_data)
    {
        log(IO_PROTOCOL,(char *)"Application Message accepted as Early Data\n\n",EARLY->val,0,NULL);
        return TLS_EARLY_DATA_ACCEPTED;
    }
    return TLS_SUCCESS;
}

// connect to server
// first try resumption if session has a good ticket attached
bool TLS13_connect(TLS_session *session,octad *EARLY)
{
    int rtn=0;
    bool early_went=false;
    session->status=TLS13_HANDSHAKING;
    if (ticket_still_good(&session->T))
    { // have a good ticket? Try it.
        rtn=TLS13_resume(session,EARLY);
        if (rtn==TLS_EARLY_DATA_ACCEPTED) early_went=true;
    } else {
        log(IO_PROTOCOL,(char *)"Resumption Ticket not found or invalid\n",NULL,0,NULL);
        rtn=TLS13_full(session);
    }
    initTicketContext(&session->T); // clear out any ticket
    
    if (rtn==0)  // failed to connect
    {
        //session->status=TLS13_DISCONNECTED;
        return false;
    }
    
    if (!early_went && EARLY!=NULL)
        TLS13_send(session,EARLY);  // didn't go early, so send it now
    session->status=TLS13_CONNECTED;
    return true;   // exiting with live session, ready to receive fresh ticket
}

// send a message post-handshake
void TLS13_send(TLS_session *state,octad *GET)
{
    log(IO_APPLICATION,(char *)"Sending Application Message\n\n",GET->val,0,NULL);
    sendClientMessage(state,APPLICATION,TLS1_2,GET,NULL,true);
}

// Process Server records received post-handshake
// Should be mostly application data, but..
// could be more handshake data disguised as application data
// For example could include a ticket. Or key update.
// returns +ve length of message, or negative error, or 0 for a time-out
int TLS13_recv(TLS_session *session,octad *REC)
{
    ret r;
    int nb,len,type,nticks,kur,rtn;//,ptr=0;
    bool fin=false;
    bool have_suitable_cert=false;
    octad TICK;        // Ticket raw data
    char fh[TLS_MAX_HASH];
    octad FH={0,sizeof(fh),fh};  // Transcript hash
    char chf[TLS_MAX_HASH];                           
    octad CHF={0,sizeof(chf),chf};      // client verify
    int hashtype=SAL_hashType(session->cipher_suite);
    TICK.len=0;
    session->ptr=0;
    nticks=0;        // number of tickets received
    OCT_kill(REC);
    log(IO_PROTOCOL,(char *)"Waiting for Server input \n",NULL,0,NULL);
    while (1)
    {
        //log(IO_PROTOCOL,(char *)"Waiting for Server input \n",NULL,0,NULL);
        OCT_kill(&session->IBUFF); session->ptr=0;
        type=getServerRecord(session);  // get first fragment to determine type
        if (type<0)
        {
            sendAlert(session,alert_from_cause(type));
            return type;   // its an error
        }
        if (type==TIMED_OUT)
        {
            log(IO_PROTOCOL,(char *)"TIME_OUT\n",NULL,0,NULL);
            return 0;
        }
        if (type==HSHAKE)
        {
            while (1)
            {

                r=parseIntorPull(session,1); nb=r.val; if (r.err) break; 
                session->ptr-=1; // peek ahead

                switch (nb)
                {
                case TICKET :   // keep last ticket
                    r=parseIntorPull(session,1); if (r.err) break;
                    r=parseIntorPull(session,3); len=r.val; if (r.err) break;   // message length
                    r=parseoctadorPullptrX(session,&TICK,len);    // just copy out pointer to this
                    nticks++;
                    rtn=parseTicket(&TICK,(unsign32)millis(),&session->T);       // extract into ticket structure T, and keep for later use  
                    if (rtn==BAD_TICKET) {
                        session->T.valid=false;
                        log(IO_PROTOCOL,(char *)"Got a bad ticket ",NULL,0,NULL);
                    } else {
                        session->T.cipher_suite=session->cipher_suite;
                        session->T.favourite_group=session->favourite_group;
                        session->T.valid=true;
                        log(IO_PROTOCOL,(char *)"Got a ticket with lifetime (minutes)= ",(char *)"%d",session->T.lifetime/60,NULL);
                    }

                    if (session->ptr==session->IBUFF.len) fin=true; // record finished
                    if (fin) break;
                    continue;

               case KEY_UPDATE :
                    r=parseIntorPull(session,1); if (r.err) break;
                    r=parseIntorPull(session,3); len=r.val; if (r.err) break;   // message length

                    if (len!=1)
                    {
                        log(IO_PROTOCOL,(char *)"Something wrong\n",NULL,0,NULL);
                        sendAlert(session,DECODE_ERROR);
                        return BAD_RECORD;
                    }
                    r=parseIntorPull(session,1); kur=r.val; if (r.err) break;
                    if (kur==TLS13_UPDATE_NOT_REQUESTED)
                    {
                        deriveUpdatedKeys(&session->K_recv,&session->STS);  // reset record number
                        log(IO_PROTOCOL,(char *)"RECEIVING KEYS UPDATED\n",NULL,0,NULL);
                    }
                    if (kur==TLS13_UPDATE_REQUESTED)
                    {
                        deriveUpdatedKeys(&session->K_recv,&session->STS);
                        sendKeyUpdate(session,TLS13_UPDATE_NOT_REQUESTED); // tell server to update their receiving keys
                        log(IO_PROTOCOL,(char *)"SENDING KEYS UPDATED\n",NULL,0,NULL);
                        log(IO_PROTOCOL,(char *)"Key update notified - client should do the same\n",NULL,0,NULL);
                        log(IO_PROTOCOL,(char *)"RECEIVING KEYS UPDATED\n",NULL,0,NULL);
                    }
                    if (kur!=TLS13_UPDATE_NOT_REQUESTED && kur!=TLS13_UPDATE_REQUESTED)
                    {
                        log(IO_PROTOCOL,(char *)"Bad Request Update value\n",NULL,0,NULL);
                        sendAlert(session,ILLEGAL_PARAMETER);
                        return BAD_REQUEST_UPDATE;
                    }
                    if (session->ptr==session->IBUFF.len) fin=true; // record finished
                    if (fin) break;
                    continue;
#ifdef POST_HS_AUTH
                case CERT_REQUEST:
                    r=getCertificateRequest(session,true);
                    if (badResponse(session,r)) {
                        return BAD_MESSAGE;
                    }
                    have_suitable_cert=false;
                    if (r.val==CERT_REQUEST) {
                        have_suitable_cert=true;
                    }
                    // send client credentials
                    if (have_suitable_cert) {
                        TLS13_client_trust(session);
                    } else {
                        sendClientCertificateChain(session,NULL);
                    }
                    transcriptHash(session,&FH);
// create client verify data and send it to Server
                    deriveVeriferData(hashtype,&CHF,&session->CTS,&FH);  
                    sendClientFinish(session,&CHF);  
                    if (session->ptr==session->IBUFF.len) fin=true; // record finished
                    if (fin) break;
                    continue;
#endif                

                default:
                    r=parseIntorPull(session,1); if (r.err) break;
                    r=parseIntorPull(session,3); len=r.val; if (r.err) break;   // message length
                    log(IO_PROTOCOL,(char *)"Unsupported Handshake message type ",(char *)"%x",nb,NULL);
                    sendAlert(session,UNEXPECTED_MESSAGE);
                    return WRONG_MESSAGE;
                    //fin=true;
                    //break;            
                }
                if (r.err || fin) break;
            }
            if (r.err) {
                sendAlert(session,alert_from_cause(r.err));
                return r.err;
            }
        }
        if (type==APPLICATION)
        { // application data received - return it
            if (session->IBUFF.len==0) continue; // empty application message
            OCT_copy(REC,&session->IBUFF);
            break;
        }
        if (type==HEART_BEAT)
        {
            REC->len=0;
            int len=session->IBUFF.len;
            int mode=session->IBUFF.val[0];
            int paylen=256*(int)(unsigned char)session->IBUFF.val[1]+(int)(unsigned char)session->IBUFF.val[2];
            if (len>18+paylen && len<256) { // looks OK - ignore if too large
                if (mode==1) { // request
                    if (session->expect_heartbeats) {
                        char resp[256];
                        octad RESP={0,sizeof(resp),resp};
                        RESP.len=len;
                        RESP.val[0]=2; // convert it to a response and bounce it back
                        for (int i=1;i<paylen+3;i++) {
                            RESP.val[i]=session->IBUFF.val[i];
                        }
                        for (int i=paylen+3;i<len;i++) {
                            RESP.val[i]=SAL_randomByte();
                        }
//printf("Received heart-beat request  - sending response\n");
                        sendRecord(session,HEART_BEAT,TLS1_2,&RESP,true);
                    }
                }
                if (mode==2) { // response
                    if (session->heartbeat_req_in_flight && paylen==0) { // if I asked for one, and the payload length is zero
                        session->heartbeat_req_in_flight=false; // reset it
                        OCT_kill(&session->IBUFF);
//printf("Received heart-beat response\n");
                        break; // better exit so link can be tested for liveness
                    }
                }
            }
            OCT_kill(&session->IBUFF);
        }
        if (type==ALERT)
        {
            log(IO_PROTOCOL,(char *)"*** Alert received - ",NULL,0,NULL);
            logAlert(session->IBUFF.val[1]);
            if (session->IBUFF.val[1]==CLOSE_NOTIFY) {
                TLS13_stop(session);
                return CLOSURE_ALERT_RECEIVED;
            } else return ERROR_ALERT_RECEIVED;    // Alert received
        }
    }

    if (session->T.valid)
    { // if ticket received, recover PSK
        recoverPSK(session); // recover PSK using NONCE and RMS, and store it with ticket
        session->T.origin=TLS_FULL_HANDSHAKE;
    } else {
        log(IO_PROTOCOL,(char *)"No ticket provided \n",NULL,0,NULL);
    }

    return REC->len;
}

// Send a heart-beat request, and wait for an immediate response
// if heartbeats not permitted, same as recv()
int TLS13_recv_and_check(TLS_session *session,octad *REC)
{
    sendHeartbeatRequest(session);
    int r=TLS13_recv(session,REC);
    if (r!=0) { // its a regular response - line is alive, heartbeat response will come later and be ignored
            return r;
    }
// it may be heart-beat response that has been received, or we may just have timed out
    if (session->heartbeat_req_in_flight) { // its not been reset, nothing received, line has gone dead
        session->status=TLS13_DISCONNECTED;
        return TIME_OUT;
    }
    return 0; // its alive, but nothing has been received. This parrot is not dead, its just resting.
}

// clean up buffers, kill crypto keys
void TLS13_clean(TLS_session *session)
{
    OCT_kill(&session->IBUFF);
    OCT_kill(&session->OBUFF);
    OCT_kill(&session->CTS);
    OCT_kill(&session->STS);
    OCT_kill(&session->RMS);
    OCT_kill(&session->HS);
    initCryptoContext(&session->K_send);
    initCryptoContext(&session->K_recv);
    session->status=TLS13_DISCONNECTED;
}

void TLS13_stop(TLS_session *session)
{
    sendAlert(session,CLOSE_NOTIFY);
}

void TLS13_end(TLS_session *session)
{    
    TLS13_clean(session);
    endTicketContext(&session->T);
#ifdef SHALLOW_STACK
    free(session->IBUFF.val);
    free(session->OBUFF.val);
#endif
}
