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

567 lines
17 KiB
C++

//
// Process output sent to Server
//
#include "tls_client_send.h"
#include "tls_logger.h"
#include "tls_certs.h"
// send Change Cipher Suite - helps get past middleboxes (WTF?)
void sendCCCS(TLS_session *session)
{
char cccs[10];
octad CCCS={0,sizeof(cccs),cccs};
OCT_from_hex(&CCCS,(char *)"140303000101");
sendOctad(session->sockptr,&CCCS);
}
// Functions to build clientHello Extensions based on our preferences/capabilities
// Build Heartbeat Extension
void addHeartbeat(octad *EXT)
{
OCT_append_int(EXT,HEARTBEAT,2); // This extension is HEARTBEAT
OCT_append_int(EXT,1,2);
#ifdef PEER_CAN_HEARTBEAT
OCT_append_int(EXT,1,1); // peer can heartbeat
#else
OCT_append_int(EXT,2,1); // peer cannot heartbeat
#endif
}
// Build Certificate Authorities Extension
/*
void addCertAuthorities(octad *EXT)
{
char dn[256];
octad DN={0,sizeof(dn),dn};
OCT_from_base64(&DN,(char *)mysupportedca);
OCT_append_int(EXT,CERT_AUTHORITIES,2);
OCT_append_int(EXT,DN.len+2,2); // only one CA allowed (for now)
OCT_append_int(EXT,DN.len,2);
OCT_append_octad(EXT,&DN);
}
*/
// Build Servername Extension
void addServerNameExt(octad *EXT,char *servername)
{
int len=strlen(servername);
OCT_append_int(EXT,SERVER_NAME,2); // This extension is SERVER_NAME(0)
OCT_append_int(EXT,5+len,2); // In theory its a list..
OCT_append_int(EXT,3+len,2); // but only one entry
OCT_append_int(EXT,0,1); // Server is of type DNS Hostname (only one type supported, and only one of each type)
OCT_append_int(EXT,len,2); // serverName length
OCT_append_string(EXT,servername); // servername
}
// Build Supported Groups Extension
void addSupportedGroupsExt(octad *EXT,int nsg,int *supportedGroups)
{
OCT_append_int(EXT,SUPPORTED_GROUPS,2); // This extension is SUPPORTED GROUPS(0x0a)
OCT_append_int(EXT,2*nsg+2,2); // Total length
OCT_append_int(EXT,2*nsg,2); // Number of entries
for (int i=0;i<nsg;i++) // One entry per supported group
OCT_append_int(EXT,supportedGroups[i],2);
}
// Build client cert type extension for raw public key
void addClientRawPublicKey(octad *EXT)
{
OCT_append_int(EXT,CLIENT_CERT_TYPE,2);
OCT_append_int(EXT,3,2);
OCT_append_int(EXT,2,1);
OCT_append_int(EXT,RAW_PUBLIC_KEY,1);
OCT_append_int(EXT,X509_CERT,1);
}
// Build server cert type extension for raw public key
void addServerRawPublicKey(octad *EXT)
{
OCT_append_int(EXT,SERVER_CERT_TYPE,2);
OCT_append_int(EXT,3,2);
OCT_append_int(EXT,2,1);
OCT_append_int(EXT,RAW_PUBLIC_KEY,1);
OCT_append_int(EXT,X509_CERT,1);
}
// Build Signature algorithms Extension
void addSigAlgsExt(octad *EXT,int nsa,int *sigAlgs)
{
OCT_append_int(EXT,SIG_ALGS,2); // This extension is SIGNATURE_ALGORITHMS(0x0d)
OCT_append_int(EXT,2*nsa+2,2); // Total length
OCT_append_int(EXT,2*nsa,2); // Number of entries
for (int i=0;i<nsa;i++) // One entry per supported signature algorithm
OCT_append_int(EXT,sigAlgs[i],2);
}
// Build Signature algorithms Cert Extension
void addSigAlgsCertExt(octad *EXT,int nsac,int *sigAlgsCert)
{
OCT_append_int(EXT,SIG_ALGS_CERT,2); // This extension is SIGNATURE_ALGORITHMS_CERT (0x32)
OCT_append_int(EXT,2*nsac+2,2); // Total length
OCT_append_int(EXT,2*nsac,2); // Number of entries
for (int i=0;i<nsac;i++) // One entry per supported signature algorithm
OCT_append_int(EXT,sigAlgsCert[i],2);
}
// Add Pre-Shared-Key ...
// ..but omit binding
int addPreSharedKeyExt(octad *EXT,unsign32 age,octad *IDS,int sha)
{
int tlen1,tlen2;
tlen1=tlen2=0;
tlen1+=IDS->len+2+4;
tlen2+=sha+1;
OCT_append_int(EXT,PRESHARED_KEY,2);
OCT_append_int(EXT,tlen1+tlen2+4,2);
// PSK Identifiers
OCT_append_int(EXT,tlen1,2);
OCT_append_int(EXT,IDS->len,2);
OCT_append_octad(EXT,IDS);
OCT_append_int(EXT,age,4);
return tlen2+2; // length of binders
}
// Add Client Key Share extension
// Offer just one public key
void addKeyShareExt(octad *EXT,int alg,octad *PK)
{
int tlen=PK->len+4;
OCT_append_int(EXT,KEY_SHARE,2); // This extension is KEY_SHARE(0x0033)
OCT_append_int(EXT,tlen+2,2);
OCT_append_int(EXT,tlen,2);
OCT_append_int(EXT,alg,2);
OCT_append_int(EXT,PK->len,2);
OCT_append_octad(EXT,PK);
}
// Add ALPN extension
// Offer just one option
void addALPNExt(octad *EXT,octad *AP)
{
int tlen=AP->len+1;
OCT_append_int(EXT,APP_PROTOCOL,2);
OCT_append_int(EXT,tlen+2,2);
OCT_append_int(EXT,tlen,2);
OCT_append_int(EXT,AP->len,1);
OCT_append_octad(EXT,AP);
}
// indicate supported PSK mode
void addPSKModesExt(octad *EXT,int mode)
{
OCT_append_int(EXT,PSK_MODE,2);
OCT_append_int(EXT,2,2);
OCT_append_int(EXT,1,1);
OCT_append_int(EXT,mode,1);
}
// indicate preferred maximum fragment length
void addMFLExt(octad *EXT,int mode)
{
if (mode>0)
{
OCT_append_int(EXT,MAX_FRAG_LENGTH,2);
OCT_append_int(EXT,1,2);
OCT_append_int(EXT,mode,1);
}
}
// indicate preferred maximum record size
void addRSLExt(octad *EXT,int size)
{
OCT_append_int(EXT,RECORD_SIZE_LIMIT,2);
OCT_append_int(EXT,2,2);
OCT_append_int(EXT,size,2);
}
// add n padding bytes
void addPadding(octad *EXT,int n)
{
OCT_append_int(EXT,PADDING,2);
OCT_append_int(EXT,n,2);
OCT_append_byte(EXT,0,n);
}
// indicate TLS version support
void addVersionExt(octad *EXT,int version)
{
OCT_append_int(EXT,TLS_VER,2);
OCT_append_int(EXT,3,2);
OCT_append_int(EXT,2,1);
OCT_append_int(EXT,version,2);
}
// Add a cookie - useful for handshake resumption
void addCookieExt(octad *EXT,octad *CK)
{
OCT_append_int(EXT,COOKIE,2);
OCT_append_int(EXT,CK->len,2);
OCT_append_octad(EXT,CK);
}
// indicate desire to send early data
void addEarlyDataExt(octad *EXT)
{
OCT_append_int(EXT,EARLY_DATA,2);
OCT_append_int(EXT,0,2);
}
// indicate willingness to do post handshake authentication
void addPostHSAuth(octad *EXT)
{
OCT_append_int(EXT,POST_HANDSHAKE_AUTH,2);
OCT_append_int(EXT,0,2);
}
// build cipher-suites octad from ciphers we support
int cipherSuites(octad *CS,int ncs,int *ciphers)
{
OCT_kill(CS);
OCT_append_int(CS,2*ncs,2);
for (int i=0;i<ncs;i++)
OCT_append_int(CS,ciphers[i],2);
return CS->len;
}
void sendZeroRecord(TLS_session *session) {
char rh[5];
char tag[TLS_MAX_TAG_SIZE];
octad TAG={0,sizeof(tag),tag};
int taglen=session->K_send.taglen;
int ctlen=TLS_MAX_OUTPUT_RECORD_SIZE+1;
int reclen=ctlen+taglen;
rh[0]=APPLICATION;
rh[1]=(TLS1_2/256);
rh[2]=(TLS1_2%256);
rh[3]=(reclen/256);
rh[4]=(reclen%256);
session->OBUFF.val[5]=APPLICATION;
session->OBUFF.len=ctlen+5;
SAL_aeadEncrypt(&session->K_send,5,rh,ctlen,&session->OBUFF.val[5],&TAG);
incrementCryptoContext(&session->K_send); // increment IV
OCT_append_octad(&session->OBUFF,&TAG);
for (int j=0;j<5;j++)
session->OBUFF.val[j]=rh[j];
sendOctad(session->sockptr,&session->OBUFF); // transmit it
OCT_kill(&session->OBUFF); // empty it
session->OBUFF.len=5;
}
// send one or more records, maybe encrypted.
void sendRecord(TLS_session *session,int rectype,int version,octad *DATA,bool flush) {
char rh[5];
int alen;
if (session->OBUFF.len==0)
{ // first time - reserve 5 spaces for header
session->OBUFF.len=5;
alen=0;
} else {
alen=session->OBUFF.len-5; // payload length
}
for (int i=0;i<DATA->len;i++) {
OCT_append_byte(&session->OBUFF,DATA->val[i],1); alen++;
bool flushing=false;
if (i==DATA->len-1 && flush) flushing=true;;
if (alen==TLS_MAX_OUTPUT_RECORD_SIZE || flushing)
{
int reclen,ctlen;
if (!session->K_send.active) { // no encryption
reclen=alen;
rh[0]=rectype;
rh[1]=(version/256);
rh[2]=(version%256);
rh[3]=(reclen/256);
rh[4]=(reclen%256);
} else {
char tag[TLS_MAX_TAG_SIZE];
octad TAG={0,sizeof(tag),tag};
int taglen=session->K_send.taglen;
OCT_append_byte(&session->OBUFF,rectype,1);
#ifdef PAD_SHORT_RECORDS
ctlen=TLS_MAX_OUTPUT_RECORD_SIZE+1; // pad to full length - should be padded with 0s
session->OBUFF.len=ctlen+5;
#else
ctlen=alen+1;
#endif
reclen=ctlen+taglen;
rh[0]=APPLICATION;
rh[1]=(TLS1_2/256);
rh[2]=(TLS1_2%256);
rh[3]=(reclen/256);
rh[4]=(reclen%256);
SAL_aeadEncrypt(&session->K_send,5,rh,ctlen,&session->OBUFF.val[5],&TAG);
incrementCryptoContext(&session->K_send); // increment IV
OCT_append_octad(&session->OBUFF,&TAG);
}
for (int j=0;j<5;j++)
session->OBUFF.val[j]=rh[j];
sendOctad(session->sockptr,&session->OBUFF); // transmit it
OCT_kill(&session->OBUFF); // empty it
alen=0;
session->OBUFF.len=5;
}
if (flushing) session->OBUFF.len=0;
}
}
// ALL Client to Server output goes via this function
// Send a client message CM|EXT (as a single record).
// Only transmit (flush) on a key change, or end of pass
void sendClientMessage(TLS_session *session,int rectype,int version,octad *CM,octad *EXT,bool flush)
{
if (session->status==TLS13_DISCONNECTED) {
return;
}
bool choice=flush;
#ifndef MERGE_MESSAGES
choice=true;
#endif
if (EXT!=NULL)
{
sendRecord(session,rectype,version,CM,false);
sendRecord(session,rectype,version,EXT,choice);
} else {
sendRecord(session,rectype,version,CM,choice);
}
}
// Send a heartbeat request record. Note my payloads are always of length 0.
// should it be encrypted? Yes
void sendHeartbeatRequest(TLS_session *session)
{
char hb[20];
octad HB={0,sizeof(hb),hb};
if (session->status==TLS13_DISCONNECTED || !session->allowed_to_heartbeat || session->heartbeat_req_in_flight) {
return;
}
//printf("Sending HEART_BEAT REQ\n");
OCT_append_int(&HB,1,1); // heartbeat request
OCT_append_int(&HB,0,2); // zero payload
for (int i=0;i<16;i++)
OCT_append_int(&HB,SAL_randomByte(),1);
session->heartbeat_req_in_flight=true;
sendRecord(session,HEART_BEAT,TLS1_2,&HB,true);
}
// build and transmit unencrypted client hello. Append pre-prepared extensions
void sendClientHello(TLS_session *session,int version,octad *CH,octad *CRN,bool already_agreed,octad *EXTENSIONS,int extra,bool resume,bool flush)
{
char cs[2+TLS_MAX_CIPHER_SUITES*2];
octad CS = {0, sizeof(cs), cs};
int nsc;
int ciphers[TLS_MAX_CIPHER_SUITES];
int compressionMethods=0x0100;
int total=8;
int extlen=EXTENSIONS->len+extra;
nsc=SAL_ciphers(ciphers);
if (already_agreed)
{ // cipher suite already agreed
nsc=1;
ciphers[0]=session->cipher_suite;
}
total+=32; // Random bytes clientRandom(&RN);
total+=33;
if (!resume) { // if its a handshake resumption, re-use the old id?? Since its the same session?
for (int i=0;i<32;i++)
session->id[i]=SAL_randomByte();
}
total+=cipherSuites(&CS,nsc,ciphers);
OCT_kill(CH);
OCT_append_byte(CH,CLIENT_HELLO,1); // clientHello handshake message // 1
OCT_append_int(CH,total+extlen-2,3); // 3
OCT_append_int(CH,TLS1_2,2); // 2
OCT_append_octad(CH,CRN); // 32
OCT_append_byte(CH,32,1); // 1
OCT_append_bytes(CH,session->id,32); // 32
OCT_append_octad(CH,&CS); // 2+TLS_MAX_CIPHER_SUITES*2
OCT_append_int(CH,compressionMethods,2); // 2
OCT_append_int(CH,extlen,2); // 2
// transmit it
sendClientMessage(session,HSHAKE,version,CH,EXTENSIONS,flush);
}
// Send "binder",
void sendBinder(TLS_session *session,octad *BND)
{
char b[TLS_MAX_HASH+3];
octad B={0,sizeof(b),b};
int tlen2=BND->len+1;
//OCT_kill(B);
OCT_append_int(&B,tlen2,2);
OCT_append_int(&B,BND->len,1);
OCT_append_octad(&B,BND);
runningHash(session,&B);
sendClientMessage(session,HSHAKE,TLS1_2,&B,NULL,true);
}
// send client alert - might be encrypted if send!=NULL
void sendAlert(TLS_session *session,int type)
{
char pt[2];
octad PT={0,sizeof(pt),pt};
OCT_append_byte(&PT,0x02,1); // alerts are always fatal
OCT_append_byte(&PT,type,1); // alert type
OCT_kill(&session->IBUFF); session->ptr=0;
sendClientMessage(session,ALERT,TLS1_2,&PT,NULL,true);
if (session->status!=TLS13_DISCONNECTED)
{
log(IO_PROTOCOL,(char *)"Alert sent to Server - ",NULL,0,NULL);
logAlert(type);
}
session->status=TLS13_DISCONNECTED; // write side of connection is now off
}
void sendKeyUpdate(TLS_session *session,int type)
{
char up[5];
octad UP={0,sizeof(up),up};
OCT_append_byte(&UP,KEY_UPDATE,1);
OCT_append_int(&UP,1,3);
OCT_append_int(&UP,type,1);
OCT_kill(&session->IBUFF); session->ptr=0;
sendClientMessage(session,HSHAKE,TLS1_2,&UP,NULL,true); // sent using old keys
deriveUpdatedKeys(&session->K_send,&session->CTS); // now update keys
log(IO_PROTOCOL,(char *)"KEY UPDATE REQUESTED\n",NULL,0,NULL);
}
// Send final client handshake verification data
void sendClientFinish(TLS_session *session,octad *CHF)
{
char pt[4];
octad PT={0,sizeof(pt),pt};
OCT_append_byte(&PT,FINISHED,1); // indicates handshake message "client finished"
OCT_append_int(&PT,CHF->len,3); // .. and its length
runningHash(session,&PT);
runningHash(session,CHF);
sendClientMessage(session,HSHAKE,TLS1_2,&PT,CHF,true); // now we can flush
}
/* Send Client Cert Verify */
void sendClientCertVerify(TLS_session *session, int sigAlg, octad *CCVSIG)
{
char pt[10];
octad PT={0,sizeof(pt),pt};
OCT_append_byte(&PT,CERT_VERIFY,1);
OCT_append_int(&PT,4+CCVSIG->len,3);
OCT_append_int(&PT,sigAlg,2);
OCT_append_int(&PT,CCVSIG->len,2);
runningHash(session,&PT);
runningHash(session,CCVSIG);
sendClientMessage(session,HSHAKE,TLS1_2,&PT,CCVSIG,false);
}
// Send Client Certificate
void sendClientCertificateChain(TLS_session *session,octad *CERTCHAIN)
{
char pt[50];
octad PT={0,sizeof(pt),pt};
OCT_append_byte(&PT,CERTIFICATE,1);
if (CERTCHAIN==NULL) { // no acceptable certificate available
OCT_append_int(&PT,4,3);
int nb=session->CTX.len;
OCT_append_byte(&PT,nb,1); // cert context
if (nb>0)
OCT_append_octad(&PT,&session->CTX);
OCT_append_int(&PT,0,3); // zero length
runningHash(session,&PT);
sendClientMessage(session,HSHAKE,TLS1_2,&PT,NULL,true);
} else {
OCT_append_int(&PT,4+CERTCHAIN->len,3);
int nb=session->CTX.len;
OCT_append_byte(&PT,nb,1); // cert context
if (nb>0)
OCT_append_octad(&PT,&session->CTX);
OCT_append_int(&PT,CERTCHAIN->len,3); // length of certificate chain
runningHash(session,&PT);
runningHash(session,CERTCHAIN);
sendClientMessage(session,HSHAKE,TLS1_2,&PT,CERTCHAIN,false);
}
}
// if early data was accepted, send this to indicate early data is finished
void sendEndOfEarlyData(TLS_session *session)
{
char ed[4];
octad ED={0,sizeof(ed),ed};
OCT_append_byte(&ED,END_OF_EARLY_DATA,1);
OCT_append_int(&ED,0,3);
runningHash(session,&ED);
sendClientMessage(session,HSHAKE,TLS1_2,&ED,NULL,true); // change of encryption keys coming, so flush
}
//
// map causes to alerts
//
int alert_from_cause(int rtn)
{
switch (rtn)
{
case NOT_TLS1_3:
return ILLEGAL_PARAMETER;
case ID_MISMATCH:
return ILLEGAL_PARAMETER;
case UNRECOGNIZED_EXT:
return ILLEGAL_PARAMETER;
case BAD_HELLO:
return ILLEGAL_PARAMETER;
case WRONG_MESSAGE: // Cause
return UNEXPECTED_MESSAGE; // Alert
case BAD_CERT_CHAIN: // Cause
return BAD_CERTIFICATE; // Alert
case MISSING_REQUEST_CONTEXT:
return ILLEGAL_PARAMETER;
case AUTHENTICATION_FAILURE:
return BAD_RECORD_MAC;
case BAD_RECORD:
return DECODE_ERROR;
case BAD_TICKET:
return ILLEGAL_PARAMETER;
case NOT_EXPECTED:
return UNSUPPORTED_EXTENSION;
case CA_NOT_FOUND:
return UNKNOWN_CA;
case CERT_OUTOFDATE:
return CERTIFICATE_EXPIRED;
case MEM_OVERFLOW:
return DECODE_ERROR;
case FORBIDDEN_EXTENSION:
return ILLEGAL_PARAMETER;
case MAX_EXCEEDED:
return RECORD_OVERFLOW;
case CERT_VERIFY_FAIL:
return DECRYPT_ERROR;
case BAD_HANDSHAKE:
return HANDSHAKE_FAILURE;
case BAD_REQUEST_UPDATE:
return ILLEGAL_PARAMETER;
case MISSING_EXTENSIONS:
return MISSING_EXTENSION;
case BAD_MESSAGE:
case EMPTY_CERT_CHAIN:
return DECODE_ERROR;
default:
return ILLEGAL_PARAMETER;
}
}