MastersThesis/PQ_TIIGER_TLS/lib/tls_client_recv.cpp

976 lines
30 KiB
C++
Raw Permalink Normal View History

2024-04-15 09:53:30 +00:00
//
// Process input received from Server
//
#include "tls_client_recv.h"
#include "tls_cert_chain.h"
#include "tls_logger.h"
// check for malformed input
bool malformed(int rval,int maxm) {
if ((rval&1) == 1) { // if its odd -> error
return true;
}
if (rval/2 > maxm) { // if its too big -> error
return true;
}
return false;
}
// First some functions for parsing values out of an octad string
// parse out an octad of length len from octad M into E
// ptr is a moving pointer through the octad M
ret parseoctad(octad *E,int len,octad *M,int &ptr)
{
ret r={0,BAD_RECORD};
if (ptr+len>M->len) return r; // not enough in M - probably need to read in some more
if (E==NULL)
{
ptr+=len;
} else {
E->len=len;
for (int i=0;i<len && i<E->max;i++ )
E->val[i]=M->val[ptr++];
}
r.val=len; r.err=0; // it looks OK
return r;
}
// parse out an array of bytes of length len
ret parsebytes(char *e,int len,octad *M,int &ptr)
{
ret r={0,BAD_RECORD};
if (ptr+len>M->len) return r; // not enough in M - probably need to read in some more
if (e==NULL)
{
ptr+=len;
} else {
for (int i=0;i<len;i++ )
e[i]=M->val[ptr++];
}
r.val=len; r.err=0; // it looks OK
return r;
}
// parse out an octad of length len from octad M
// ptr is a moving pointer through the octad M
// but now E is just a pointer into M
ret parseoctadptr(octad *E,int len,octad *M,int &ptr)
{
ret r={0,BAD_RECORD};
if (ptr+len>M->len) return r; // not enough in M - probably need to read in some more
E->len=len;
E->max=len;
E->val=&M->val[ptr];
ptr+=len;
r.val=len; r.err=0; // it looks OK
return r;
}
// parse out an integer of length len
ret parseInt(octad *M,int len,int &ptr)
{
ret r={0,BAD_RECORD};
if (ptr+len>M->len) return r;
r.val=0;
for (int i=0;i<len;i++)
r.val=256*r.val+(unsigned int)(unsigned char)M->val[ptr++];
r.err=0;
return r;
}
// ALL Server to Client records arrive via this function
// Basic function for reading in a record, which may be only contain a fragment of a larger message
// Protocol messages can be fragmented, and arrive as multiple records.
// Record contents are appended to the input buffer.
// Messages are read from the input buffer, and on reaching the end of the buffer,
// new records are pulled in to complete a message.
// Most records must be decrypted before being appended to the message buffer.
// get another fragment of server response, in the form of a record. Record type encoded in its header
// output message body to IBUFF
// If record is encrypted, decrypt and authenticate it
// append its contents to the end of IBUFF
// returns record type, ALERT, APPLICATION or HSHAKE (or pseudo type TIMED_OUT)
int getServerRecord(TLS_session *session)
{
int rtn,left,pos,rlen,taglen;
char rh[5];
octad RH={0,sizeof(rh),rh};
char tag[TLS_MAX_TAG_SIZE];
octad TAG={0,sizeof(tag),tag};
pos=session->IBUFF.len; // current end of IO
rtn=getOctad(session->sockptr,&RH,3); // Get record Header - should be something like 17 03 03 XX YY
// Need to check RH.val for correctness
if (rtn<0)
return TIMED_OUT;
if (RH.val[0]==ALERT)
{ // plaintext alert
left=getInt16(session->sockptr);
if (left!=2) return BAD_RECORD; // ** RM
rtn=getOctad(session->sockptr,&session->IBUFF,left);
if (rtn<0)
return TIMED_OUT;
return ALERT;
}
if (RH.val[0]==CHANGE_CIPHER)
{ // read it, and ignore it
char sccs[10];
left=getInt16(session->sockptr);
if (left!=1) return BAD_RECORD; // ** RM
rtn=getBytes(session->sockptr,sccs,left);
if (rtn<0)
return TIMED_OUT;
if (session->status!=TLS13_HANDSHAKING)
return WRONG_MESSAGE;
rtn=getOctad(session->sockptr,&RH,3); // get the next record and carry on
if (rtn<0)
return TIMED_OUT;
}
if (RH.val[0]!=HSHAKE && RH.val[0]!=APPLICATION && RH.val[0]!=HEART_BEAT)
return WRONG_MESSAGE;
left=getInt16(session->sockptr);
if (left>TLS_MAX_CIPHER_FRAG)
return MAX_EXCEEDED;
OCT_append_int(&RH,left,2);
if (left+pos>session->IBUFF.max)
{ // this commonly happens with big records of application data from server
log(IO_DEBUG,(char *)"Record received of length= ",(char *)"%d",left+pos,NULL);
return MEM_OVERFLOW; // record is too big - memory overflow
}
if (!session->K_recv.active)
{ // not encrypted
if (RH.val[0]==APPLICATION || RH.val[0]==HEART_BEAT){
return BAD_RECORD;
}
if (left>TLS_MAX_PLAIN_FRAG)
return MAX_EXCEEDED;
if (left==0)
return WRONG_MESSAGE;
rtn=getBytes(session->sockptr,&session->IBUFF.val[pos],left); // read in record body
if (rtn<0)
return TIMED_OUT;
session->IBUFF.len+=left;
return HSHAKE;
}
if (RH.val[0]==HSHAKE) {
return BAD_RECORD;
}
taglen=session->K_recv.taglen;
if (left < taglen)
return BAD_RECORD;
rlen=left-taglen; // plaintext record length
rtn=getBytes(session->sockptr,&session->IBUFF.val[pos],rlen); // read in record body
if (rtn<0)
return TIMED_OUT;
session->IBUFF.len+=(rlen); // place record into iobuff
rtn=getOctad(session->sockptr,&TAG,taglen); // extract TAG
if (rtn<0)
return TIMED_OUT;
bool success=SAL_aeadDecrypt(&session->K_recv,RH.len,RH.val,rlen,&session->IBUFF.val[pos],&TAG);
if (!success)
return AUTHENTICATION_FAILURE; // tag is wrong
incrementCryptoContext(&session->K_recv); // update IV
// get record ending - encodes real (disguised) record type. Could be an Alert.
int lb=session->IBUFF.val[session->IBUFF.len-1];
session->IBUFF.len--; rlen--; // remove it
while (lb==0 && rlen>0 /*session->IBUFF.len>0*/)
{ // could be zero padding
lb=session->IBUFF.val[session->IBUFF.len-1]; // need to track back through zero padding for this....
session->IBUFF.len--; rlen--;// remove it
}
if (rlen>TLS_MAX_PLAIN_FRAG) return MAX_EXCEEDED;
// rlen is Inner Plaintext length?
if ((lb==HSHAKE || lb==ALERT) && rlen==0)
return WRONG_MESSAGE; // Implementations MUST NOT send zero-length fragments of Handshake types
if (lb==HSHAKE)
return HSHAKE;
if (lb==APPLICATION)
return APPLICATION;
if (lb==HEART_BEAT)
return HEART_BEAT;
if (lb==ALERT)
{ // Disguised Alert record received, delete anything in IO prior to alert, and just return 2-byte alert
OCT_shift_left(&session->IBUFF,pos);
return ALERT;
}
return WRONG_MESSAGE;
}
// These functions read data from the input buffer, and pull more handshake records from a socket if it has to.
// ALL of these records SHOULD be of type HSHAKE
ret parseIntorPull(TLS_session *session,int len)
{
ret r=parseInt(&session->IBUFF,len,session->ptr);
while (r.err)
{ // not enough bytes in IO - Pull in some more
int rtn=getServerRecord(session);
if (rtn!=HSHAKE) { // Bad input from server (Authentication failure? Wrong record type?)
r.err=rtn; // probably negative error
if (rtn==ALERT) r.val=session->IBUFF.val[1];
if (rtn==APPLICATION) r.err=WRONG_MESSAGE;
break;
}
r=parseInt(&session->IBUFF,len,session->ptr);
}
return r;
}
// Get an octad O of length len from the IBUFF buffer. Create a copy.
ret parseoctadorPull(TLS_session *session,octad *O,int len)
{
ret r=parseoctad(O,len,&session->IBUFF,session->ptr);
while (r.err)
{ // not enough bytes in IBUFF - pull in another fragment
int rtn=getServerRecord(session);
if (rtn!=HSHAKE) {
r.err=rtn;
if (rtn==ALERT) r.val=session->IBUFF.val[1];
if (rtn==APPLICATION) r.err=WRONG_MESSAGE;
break;
}
r=parseoctad(O,len,&session->IBUFF,session->ptr);
}
return r;
}
// Get byte array o of length len from the IBUFF buffer. Create a copy.
ret parsebytesorPull(TLS_session *session,char *o,int len)
{
ret r=parsebytes(o,len,&session->IBUFF,session->ptr);
while (r.err)
{ // not enough bytes in IO - pull in another fragment
int rtn=getServerRecord(session);
if (rtn!=HSHAKE) {
r.err=rtn;
if (rtn==ALERT) r.val=session->IBUFF.val[1];
if (rtn==APPLICATION) r.err=WRONG_MESSAGE;
break;
}
r=parsebytes(o,len,&session->IBUFF,session->ptr);
}
return r;
}
// Get an octad O of length len from the IBUFF buffer, but this time the output octad is a pointer into the IBUFF buffer
ret parseoctadorPullptrX(TLS_session *session,octad *O,int len)
{
ret r=parseoctadptr(O,len,&session->IBUFF,session->ptr);
while (r.err)
{ // not enough bytes in IO - pull in another fragment
int rtn=getServerRecord(session);
if (rtn!=HSHAKE) {
r.err=rtn;
if (rtn==ALERT) r.val=session->IBUFF.val[1];
if (rtn==APPLICATION) r.err=WRONG_MESSAGE;
break;
}
r=parseoctadptr(O,len,&session->IBUFF,session->ptr);
}
return r;
}
// Could have (a) received an alert, or (b) had problem with response, so need to send an alert
// test for a bad response, log what happened and act accordingly
// Very probably requires sending an alert to the server, and aborting
// If Alert received, log it, send alert, abort
bool badResponse(TLS_session *session,ret r) //Socket *client,crypto *send,ret r)
{
logServerResponse(r);
if (r.err != 0)
{
log(IO_PROTOCOL,(char *)"Handshake failed\n",NULL,0,NULL);
}
if (r.err<0)
{ // send an alert to the Server, and abort
sendAlert(session,alert_from_cause(r.err));
return true;
}
if (r.err==ALERT)
{ // received an alert from the Server - abort
log(IO_PROTOCOL,(char *)"*** Alert received - ",NULL,0,NULL);
logAlert(r.val);
return true;
}
if (r.err) // some other error, maybe time out
return true;
return false;
}
// Function return convention. These functions return a "ret" structure
// if r.err +ve log unexpected event and abort
// if r.err -ve send alert and abort
// if r.err = ALERT, r.val=received alert code
// if all goes well, r.val returns message type, r.err=0
// Note: we are on a hair trigger here. Anything doesn't look right, we bomb out.
// Functions to process server responses
// Deals with any kind of fragmentation
// Build up server handshake response in IBUFF, decrypting each fragment in-place
// extract Encrypted Extensions, Certificate Chain, Server Certificate Signature and Server Verifier Data
// update transcript hash
// Bad actor Server could be throwing anything at us - so be careful
ret seeWhatsNext(TLS_session *session)
{
int nb;
ret r;
//session->ptr=0;
r=parseIntorPull(session,1);
if (r.err) return r;
session->ptr-=1;
nb=r.val;
if (nb==END_OF_EARLY_DATA || nb==KEY_UPDATE) { // Servers MUST NOT send this.... KEY_UPDATE should not happen at this stage
r.err=WRONG_MESSAGE;
return r;
}
return r;
}
// Get encrypted extensions
ret getServerEncryptedExtensions(TLS_session *session,ee_status *enc_ext_expt,ee_status *enc_ext_resp)
{
ret r;
int nb,left,ext,len,tlen,xlen,mfl,cct,sct,hbmode;//,ptr=0;
int unexp=0;
r=parseIntorPull(session,1);
if (r.err) return r;
nb=r.val;
if (nb!=ENCRYPTED_EXTENSIONS) {
r.err=WRONG_MESSAGE;
return r;
}
r=parseIntorPull(session,3); left=r.val; if (r.err) return r; // message length
enc_ext_resp->early_data=false;
enc_ext_resp->alpn=false;
enc_ext_resp->server_name=false;
enc_ext_resp->max_frag_length=false;
r=parseIntorPull(session,2); len=r.val; if (r.err) return r; // length of extensions
if (left!=len+2) {
r.err=BAD_MESSAGE;
return r;
}
// extension could include Servers preference for supported groups, which could be
// taken into account by the client for later connections. Here we will ignore it. From RFC:
// "Clients MUST NOT act upon any information found in "supported_groups" prior to successful completion of the handshake"
while (len>0)
{
r=parseIntorPull(session,2); ext=r.val; if (r.err) return r;
r=parseIntorPull(session,2); tlen=r.val; if (r.err) return r;
if (len<tlen+4) {
r.err=BAD_MESSAGE;
return r;
}
len-=(tlen+4);
switch (ext)
{
case EARLY_DATA :
if (tlen!=0) {
r.err=UNRECOGNIZED_EXT;
return r;
}
enc_ext_resp->early_data=true;
if (!enc_ext_expt->early_data) {
r.err=NOT_EXPECTED;
return r;
}
break;
case MAX_FRAG_LENGTH :
r=parseIntorPull(session,1); mfl=r.val; if (r.err) return r; // ideally this should the same as requested by client
// but server may have ignored this request... :( so we ignore this response
if (mfl!=TLS_MAX_FRAG || tlen!=1) {
r.err=UNRECOGNIZED_EXT;
return r;
}
enc_ext_resp->max_frag_length=true;
if (!enc_ext_expt->max_frag_length) {
r.err=NOT_EXPECTED;
return r;
}
break;
case CLIENT_CERT_TYPE :
r=parseIntorPull(session,1); cct=r.val; if (r.err) return r;
if (tlen!=1) {
r.err=UNRECOGNIZED_EXT;
return r;
}
#ifdef PREFER_RAW_CLIENT_PUBLIC_KEY
if (cct!=RAW_PUBLIC_KEY) {
session->client_cert_type=X509_CERT;
} else {
session->client_cert_type=RAW_PUBLIC_KEY;
}
#else
session->client_cert_type=X509_CERT;
#endif
break;
case SERVER_CERT_TYPE :
r=parseIntorPull(session,1); sct=r.val; if (r.err) return r;
if (tlen!=1) {
r.err=UNRECOGNIZED_EXT;
return r;
}
#ifdef PREFER_RAW_SERVER_PUBLIC_KEY
if (sct!=RAW_PUBLIC_KEY) {
session->server_cert_type=X509_CERT;
} else {
session->server_cert_type=RAW_PUBLIC_KEY;
}
#else
session->server_cert_type=X509_CERT;
#endif
break;
case RECORD_SIZE_LIMIT:
r=parseIntorPull(session,2); mfl=r.val; if (r.err) return r;
if (tlen!=2 || mfl<64) {
r.err=UNRECOGNIZED_EXT;
return r;
}
session->max_record=mfl;
break;
case HEARTBEAT:
r=parseIntorPull(session,1); hbmode=r.val; if (r.err) return r;
if (hbmode==0 || hbmode>2)
{
r.err=UNRECOGNIZED_EXT;
return r;
}
//printf("EXPECTING HEARTBEATs\n");
session->expect_heartbeats=true;
if (hbmode==1) {
//printf("ALLOWED TO HEARTBEAT\n");
session->allowed_to_heartbeat=true;
}
else session->allowed_to_heartbeat=false;
break;
case APP_PROTOCOL :
r=parseIntorPull(session,2); xlen=r.val; if (r.err) return r;
r=parseIntorPull(session,1); mfl=r.val; if (r.err) return r;
if (tlen!=xlen+2 || xlen!=mfl+1) // ** RM
{
r.err=UNRECOGNIZED_EXT;
return r;
}
r=parseoctadorPull(session,NULL,mfl); if (r.err) return r; // ALPN code - send to NULL -- assume its the one I asked for
enc_ext_resp->alpn=true;
if (!enc_ext_expt->alpn) {
r.err=NOT_EXPECTED;
return r;
}
break;
case SERVER_NAME:
enc_ext_resp->server_name=true;
if (tlen!=0) {
r.err=UNRECOGNIZED_EXT;
return r;
}
if (!enc_ext_expt->server_name) {
r.err=NOT_EXPECTED;
return r;
}
break;
case SIG_ALGS:
case SIG_ALGS_CERT:
case KEY_SHARE:
case PSK_MODE:
case PRESHARED_KEY:
case TLS_VER:
case COOKIE:
case PADDING:
session->ptr+=tlen; // skip over it
r.err=FORBIDDEN_EXTENSION;
return r;
default: // ignore all other extensions
session->ptr+=tlen; // skip over it
unexp++;
break;
}
if (r.err) return r;
}
// Update Transcript hash and rewind IO buffer
runningHashIOrewind(session);
if (unexp>0)
{
r.err=UNRECOGNIZED_EXT;
log(IO_DEBUG,(char *)"Unrecognized extensions received\n",NULL,0,NULL);
}
r.val=nb;
return r;
}
// check for overlap given server signature capabilities, and my client certificate
static bool overlap(int *serverSigAlgs,int nssa,int *serverCertSigAlgs,int nscsa) {
int clientCertReqs[TLS_MAX_SUPPORTED_SIGS];
int nsreq=getSigRequirements(clientCertReqs);
for (int i=0;i<nsreq;i++) {
bool itsthere=false;
int sig=clientCertReqs[i];
for (int j=0;j<nssa;j++) {
if (sig==serverSigAlgs[j]) {
itsthere=true;
}
}
for (int j=0;j<nscsa;j++) {
if (sig==serverCertSigAlgs[j]) {
itsthere=true;
}
}
if (!itsthere) {
return false;
}
}
return true;
}
// Receive a Certificate request
ret getCertificateRequest(TLS_session *session,bool context)
{
ret r;
int i,left,nb,ext,len,tlen,nssa,nscsa;//,ptr=0;
int unexp=0;
int sigalgs[TLS_MAX_SUPPORTED_SIGS]; // acceptable client signature types
int certsigalgs[TLS_MAX_SUPPORTED_SIGS];
r=parseIntorPull(session,1); // get message type
if (r.err!=0) {return r;}
nb=r.val;
if (nb != CERT_REQUEST) {
r.err=WRONG_MESSAGE;
return r;
}
r=parseIntorPull(session,3); left=r.val; if (r.err) return r; // message length
r=parseIntorPull(session,1); nb=r.val; if (r.err) return r;
if (context)
{
if (nb==0x00) {
r.err= MISSING_REQUEST_CONTEXT;// expecting Request context
return r;
}
r=parseoctadorPull(session,&session->CTX,nb); if (r.err) return r;
left-=nb;
} else {
if (nb!=0x00) {
r.err= MISSING_REQUEST_CONTEXT;// expecting 0x00 Request context
return r;
}
}
r=parseIntorPull(session,2); len=r.val; if (r.err) return r; // length of extensions
if (left!=len+3) {
r.err=BAD_MESSAGE;
return r;
}
nssa=nscsa=0;
// extension must include signature algorithms
while (len>0)
{
r=parseIntorPull(session,2); ext=r.val; if (r.err) return r;
r=parseIntorPull(session,2); tlen=r.val; if (r.err) return r;
if (len<tlen+4) {
r.err=BAD_MESSAGE;
return r;
}
len-=(4+tlen);
switch (ext)
{
case SIG_ALGS :
r=parseIntorPull(session,2); if (r.err) return r;
if (malformed(r.val,TLS_MAX_SUPPORTED_SIGS) || tlen!=2+r.val) {
r.err=UNRECOGNIZED_EXT;
return r;
}
nssa=r.val/2;
for (i=0;i<nssa;i++)
{
r=parseIntorPull(session,2); if (r.err) return r;
sigalgs[i]=r.val;
}
break;
case SIG_ALGS_CERT :
r=parseIntorPull(session,2); if (r.err) return r;
if (malformed(r.val,TLS_MAX_SUPPORTED_SIGS) || tlen!=2+r.val) {
r.err=UNRECOGNIZED_EXT;
return r;
}
nscsa=r.val/2;
for (i=0;i<nscsa;i++)
{
r=parseIntorPull(session,2); if (r.err) return r;
if (i<TLS_MAX_SUPPORTED_SIGS) certsigalgs[i]=r.val;
}
break;
default: // ignore all other extensions
session->ptr+=tlen; // skip over it
unexp++;
break;
}
if (r.err) return r;
}
// Update Transcript hash and rewind IO buffer
runningHashIOrewind(session);
r.val=CERT_REQUEST;
if (nssa==0) { // must specify at least one signature algorithm
r.err=MISSING_EXTENSIONS;
return r;
}
#if CLIENT_CERT == NOCERT
r.val=0;
#else
// just decline by sending NULL certificate, rather than an alert
if (!overlap(sigalgs,nssa,certsigalgs,nscsa)) {
r.val=0;
//log(IO_DEBUG,(char *)"Server cannot verify client certificate\n",NULL,0,NULL);
//r.err=BAD_HANDSHAKE;
//return r;
}
#endif
if (unexp>0)
log(IO_DEBUG,(char *)"Unrecognized extensions received\n",NULL,0,NULL);
return r;
}
// Get certificate chain, and check its validity
ret getCheckServerCertificateChain(TLS_session *session,octad *PUBKEY,octad *SIG)
{
ret r;
int nb,len,tlen;
octad CERTCHAIN; // // Clever re-use of memory - share memory rather than make a copy!
CERTCHAIN.len=0;
r=parseIntorPull(session,1); // get message type
if (r.err!=0) {return r;}
nb=r.val;
if (nb != CERTIFICATE) {
r.err=WRONG_MESSAGE;
return r;
}
r=parseIntorPull(session,3); len=r.val; if (r.err) return r; // message length
log(IO_DEBUG,(char *)"Certificate Chain Length= ",(char *)"%d",len,NULL);
if (len==0)
{
r.err=EMPTY_CERT_CHAIN;
return r;
}
r=parseIntorPull(session,1); nb=r.val; if (r.err) return r;
if (nb!=0x00) {
r.err=MISSING_REQUEST_CONTEXT;// expecting 0x00 Request context
return r;
}
r=parseIntorPull(session,3); tlen=r.val; if (r.err) return r; // get length of certificate chain list
if (tlen==0)
{
r.err=EMPTY_CERT_CHAIN;
return r;
}
if (tlen+4!=len)
{
r.err=BAD_CERT_CHAIN;
return r;
}
r=parseoctadorPullptrX(session,&CERTCHAIN,tlen); if (r.err) return r; // get pointer to certificate chain
// Update Transcript hash and rewind IO buffer
runningHashIO(session); // Got to do this here, as checkServerCertChain may modify IO buffer contents
r.err=checkServerCertChain(&CERTCHAIN,session->hostname,session->server_cert_type,PUBKEY,SIG);
#ifdef NO_CERT_CHECKS
r.err=0;
#endif
rewindIO(session); // now save to rewind
r.val=CERTIFICATE;
return r;
}
// Get Server proof that he owns the Certificate, by receiving its signature SCVSIG on transcript hash
ret getServerCertVerify(TLS_session *session,octad *SCVSIG,int &sigalg)
{
ret r;
int nb,left,len;//,ptr=0;
int sigAlgs[TLS_MAX_SUPPORTED_SIGS];
int nsa=SAL_sigs(sigAlgs);
r=parseIntorPull(session,1); // get message type
if (r.err!=0) {return r;}
nb=r.val;
if (nb != CERT_VERIFY) {
r.err=WRONG_MESSAGE;
return r;
}
r=parseIntorPull(session,3); left=r.val; if (r.err) return r; // message length
OCT_kill(SCVSIG);
r=parseIntorPull(session,2); sigalg=r.val; if (r.err) return r; // may for example be 0804 - RSA-PSS-RSAE-SHA256
bool offered=false;
for (int i=0;i<nsa;i++)
if (sigalg==sigAlgs[i]) offered=true;
if (!offered)
{
r.err=CERT_VERIFY_FAIL;
return r;
}
r=parseIntorPull(session,2); len=r.val; if (r.err) return r; // sig data follows
r=parseoctadorPull(session,SCVSIG,len); if (r.err) return r;
if (left!=len+4) {
r.err=BAD_MESSAGE;
return r;
}
// Update Transcript hash and rewind IO buffer
runningHashIOrewind(session);
r.val=CERT_VERIFY;
return r;
}
// Get handshake finish verifier data in HFIN
ret getServerFinished(TLS_session *session,octad *HFIN)
{
ret r;
int nb,len;
r=parseIntorPull(session,1); // get message type
if (r.err!=0) {return r;}
nb=r.val;
if (nb != FINISHED) {
r.err=WRONG_MESSAGE;
return r;
}
int hashtype=SAL_hashType(session->cipher_suite);
int hlen=SAL_hashLen(hashtype);
r=parseIntorPull(session,3); len=r.val; if (r.err) return r; // message length
OCT_kill(HFIN);
r=parseoctadorPull(session,HFIN,hlen); if (r.err) return r;
if (len!=hlen)
r.err=BAD_MESSAGE;
// Update Transcript hash and rewind IO buffer
runningHashIOrewind(session);
r.val=FINISHED;
return r;
}
// Handshake Retry Request
static const char *hrrh= (const char *)"CF21AD74E59A6111BE1D8C021E65B891C2A211167ABB8C5E079E09E2C8A8339C";
// Process initial serverHello - NOT encrypted
// pskid >=0 if Pre-Shared-Key is accepted
ret getServerHello(TLS_session *session,int &kex,octad *CK,octad *PK,int &pskid)
{
ret r;
int tls,left,silen,cmp,extLen,ext,tmplen,pklen,cipher;
bool retry=false;
char sid[32];
char srn[32];
octad SRN={0,sizeof(srn),srn};
char hrr[40];
octad HRR={0,sizeof(hrr),hrr};
// need this to check for Handshake Retry Request
OCT_from_hex(&HRR,(char *)hrrh);
kex=-1;
pskid=-1;
OCT_kill(CK); OCT_kill(PK);
// get first fragment - not encrypted
OCT_kill(&session->IBUFF);
// start parsing mandatory components
session->ptr=0;
r=parseIntorPull(session,1);
if (r.err) return r; // should be Server Hello
if (r.val!=SERVER_HELLO)
{
r.err=BAD_HELLO;
return r;
}
r=parseIntorPull(session,3); left=r.val; if (r.err) return r; // If not enough, pull in another fragment
r=parseIntorPull(session,2); if (r.err) return r;
if (left<72)
{
r.err=BAD_HELLO;
return r;
}
left-=2; // whats left in message
r= parseoctadorPull(session,&SRN,32); if (r.err) return r;
left-=32;
if (OCT_compare(&SRN,&HRR))
{
retry=true; // "random" data was not random at all - indicated Handshake Retry Request!
}
r=parseIntorPull(session,1); silen=r.val; if (silen!=32) r.err=BAD_HELLO; if (r.err) return r;
left-=1;
r=parsebytesorPull(session,sid,silen); if (r.err) return r;
left-=silen;
// Tricky one. According to the RFC (4.1.3) this check should be made, even though the session id is "legacy",
// Unfortunately it is not made clear if the same session ID should be use on a handshake resumption.
// We note that some servers echo the original id, not a new id associated with a new Client Hello
// Solution here is to use same id on resumption(?)
bool mismatch=false;
for (int i=0;i<32;i++)
{
if (session->id[i]!=sid[i])
mismatch=true;
}
if (mismatch) {
r.err=ID_MISMATCH; // check identities match
return r;
}
r=parseIntorPull(session,2); cipher=r.val; if (r.err) return r;
left-=2;
if (session->cipher_suite!=0)
{ // don't allow a change after initial assignment
if (cipher!=session->cipher_suite)
{
r.err=BAD_HELLO;
return r;
}
}
session->cipher_suite=cipher;
r=parseIntorPull(session,1); cmp=r.val; if (r.err) return r;
left-=1; // Compression not used in TLS1.3
if (cmp!=0x00) {
r.err=NOT_TLS1_3; // don't ask
return r;
}
r=parseIntorPull(session,2); extLen=r.val; if (r.err) return r;
left-=2;
if (left!=extLen) { // Check space left is size of extensions
r.err=BAD_HELLO;
return r;
}
int supported_version=0;
// process extensions
while (extLen>0)
{
r=parseIntorPull(session,2); ext=r.val; if (r.err) return r;
r=parseIntorPull(session,2); tmplen=r.val; if (r.err) break;
if (extLen<4+tmplen)
{
r.err=BAD_HELLO;
return r;
}
extLen-=(4+tmplen);
log(IO_DEBUG,(char *)"Server Hello Extension= ",(char *)"%x",ext,NULL);
switch (ext)
{
case KEY_SHARE :
{ // actually mandatory
int glen=2;
r=parseIntorPull(session,2); kex=r.val; if (r.err) break;
if (!retry)
{ // its not a retry request
r=parseIntorPull(session,2); pklen=r.val; if (r.err) break; // FIX this first for HRR
r=parseoctadorPull(session,PK,pklen);
glen+=(2+pklen);
}
if (tmplen!=glen)
{
r.err=BAD_HELLO;
return r;
}
break;
}
case PRESHARED_KEY :
{ // Indicate acceptance of pre-shared key
if (tmplen!=2)
{
r.err=BAD_HELLO;
return r;
}
r=parseIntorPull(session,2); pskid=r.val;
break;
}
case COOKIE :
{ // Pick up a cookie
r=parseoctadorPull(session,CK,tmplen);
break;
}
case TLS_VER :
{ // report TLS version
if (tmplen!=2)
{
r.err=BAD_HELLO;
return r;
}
r=parseIntorPull(session,2); tls=r.val; if (r.err) break; // get TLS version
supported_version=tls;
break;
}
default :
r.err=UNRECOGNIZED_EXT;
break;
}
if (r.err) return r;
}
if (supported_version==0 || supported_version!=TLS1_3)
r.err=NOT_TLS1_3;
if (retry)
r.val=HANDSHAKE_RETRY;
else
r.val=SERVER_HELLO;
return r;
}