// // 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;ilen+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;ilen; } 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;ilen;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; } }