MastersThesis/PQ_TIIGER_TLS/lib/tls_protocol.cpp
2024-04-19 14:16:07 +02:00

1238 lines
47 KiB
C++

//
// 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
}