444 lines
14 KiB
C++
444 lines
14 KiB
C++
//
|
|
// TLS1.3 crypto support functions (hashing, KDFs etc)
|
|
//
|
|
|
|
#include "tls_keys_calc.h"
|
|
#include "tls_logger.h"
|
|
#include "tls_x509.h"
|
|
|
|
// Initialise transcript hash
|
|
void initTranscriptHash(TLS_session *session)
|
|
{
|
|
int hashtype=SAL_hashType(session->cipher_suite);
|
|
SAL_hashInit(hashtype,&session->tlshash);
|
|
}
|
|
|
|
// Add octad to transcript hash
|
|
void runningHash(TLS_session *session,octad *O)
|
|
{
|
|
SAL_hashProcessArray(&session->tlshash,O->val,O->len);
|
|
}
|
|
|
|
// Add IO buffer to transcript hash
|
|
void runningHashIO(TLS_session *session)
|
|
{
|
|
SAL_hashProcessArray(&session->tlshash,session->IBUFF.val,session->ptr);
|
|
}
|
|
|
|
// Shift octad left - rewind IO buffer to start
|
|
void rewindIO(TLS_session *session)
|
|
{
|
|
OCT_shift_left(&session->IBUFF,session->ptr);
|
|
session->ptr=0;
|
|
}
|
|
|
|
void runningHashIOrewind(TLS_session *session)
|
|
{
|
|
runningHashIO(session);
|
|
rewindIO(session);
|
|
}
|
|
|
|
// Output transcript hash
|
|
void transcriptHash(TLS_session *session,octad *O)
|
|
{
|
|
O->len=SAL_hashOutput(&session->tlshash,O->val);
|
|
}
|
|
|
|
// special case handling for first clientHello after retry request
|
|
void runningSyntheticHash(TLS_session *session,octad *O,octad *E)
|
|
{
|
|
int htype=session->tlshash.htype;
|
|
unihash rhash;
|
|
char hh[TLS_MAX_HASH];
|
|
octad HH={0,sizeof(hh),hh};
|
|
|
|
SAL_hashInit(htype,&rhash);
|
|
// RFC 8446 - "special synthetic message"
|
|
SAL_hashProcessArray(&rhash,O->val,O->len);
|
|
SAL_hashProcessArray(&rhash,E->val,E->len);
|
|
HH.len=SAL_hashOutput(&rhash,HH.val);
|
|
|
|
char t[4];
|
|
t[0]=MESSAGE_HASH;
|
|
t[1]=t[2]=0;
|
|
t[3]=SAL_hashLen(htype);
|
|
SAL_hashProcessArray(&session->tlshash,t,4);
|
|
|
|
runningHash(session,&HH);
|
|
}
|
|
|
|
// Initialise crypto context (Key,IV, Record number)
|
|
void initCryptoContext(crypto *C)
|
|
{
|
|
C->active=false;
|
|
|
|
C->K.len = 0;
|
|
C->K.max = TLS_MAX_KEY;
|
|
C->K.val = C->k;
|
|
|
|
C->IV.len = 0;
|
|
C->IV.max = 12;
|
|
C->IV.val = C->iv;
|
|
|
|
C->suite=TLS_AES_128_GCM_SHA256; // default
|
|
C->record=0;
|
|
C->taglen=16; // default
|
|
}
|
|
|
|
// Fill a crypto context with new key/IV
|
|
void updateCryptoContext(crypto *C,octad *K,octad *IV)
|
|
{
|
|
C->active=true;
|
|
OCT_copy(&(C->K),K);
|
|
OCT_copy(&(C->IV),IV);
|
|
C->record=0;
|
|
}
|
|
|
|
// increment record, and update IV
|
|
void incrementCryptoContext(crypto *C)
|
|
{
|
|
unsigned char b[4];
|
|
b[3] = (unsigned char)(C->record);
|
|
b[2] = (unsigned char)(C->record >> 8);
|
|
b[1] = (unsigned char)(C->record >> 16);
|
|
b[0] = (unsigned char)(C->record >> 24);
|
|
for (int i=0;i<4;i++)
|
|
C->IV.val[8+i]^=b[i]; // revert to original IV
|
|
C->record++;
|
|
b[3] = (unsigned char)(C->record);
|
|
b[2] = (unsigned char)(C->record >> 8);
|
|
b[1] = (unsigned char)(C->record >> 16);
|
|
b[0] = (unsigned char)(C->record >> 24);
|
|
for (int i=0;i<4;i++)
|
|
C->IV.val[8+i]^=b[i]; // advance to new IV
|
|
}
|
|
|
|
// create expanded HKDF label LB from label and context
|
|
static void hkdfLabel(octad *LB,int length,octad *Label,octad *CTX)
|
|
{
|
|
OCT_append_int(LB,length,2); // 2
|
|
OCT_append_byte(LB,(char)(6+Label->len),1); // 1
|
|
OCT_append_string(LB,(char *)"tls13 "); // 6
|
|
OCT_append_octad(LB,Label); // Label->len
|
|
if (CTX!=NULL)
|
|
{
|
|
OCT_append_byte(LB, (char)(CTX->len), 1); // 1
|
|
OCT_append_octad(LB,CTX); // CTX->len
|
|
} else {
|
|
OCT_append_byte(LB,0,1); // 1
|
|
}
|
|
}
|
|
|
|
// HKDF extension for TLS1.3
|
|
static void hkdfExpandLabel(int htype,octad *OKM,int olen,octad *PRK,octad *Label,octad *CTX)
|
|
{
|
|
char hl[TLS_MAX_HASH+24];
|
|
octad HL={0,sizeof(hl),hl};
|
|
hkdfLabel(&HL,olen,Label,CTX);
|
|
SAL_hkdfExpand(htype,olen,OKM,PRK,&HL);
|
|
}
|
|
|
|
// create verification data
|
|
void deriveVeriferData(int htype,octad *CF,octad *CHTS,octad *H)
|
|
{
|
|
char fk[TLS_MAX_HASH];
|
|
octad FK = {0,sizeof(fk),fk};
|
|
char info[10];
|
|
octad INFO = {0,sizeof(info),info};
|
|
int hlen=SAL_hashLen(htype);
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"finished");
|
|
hkdfExpandLabel(htype,&FK,hlen,CHTS,&INFO,NULL);
|
|
SAL_hmac(htype,CF,&FK,H);
|
|
}
|
|
|
|
// check verification data
|
|
bool checkVeriferData(int htype,octad *SF,octad *SHTS,octad *H)
|
|
{
|
|
char vd[TLS_MAX_HASH];
|
|
octad VD = {0,sizeof(vd),vd};
|
|
deriveVeriferData(htype,&VD,SHTS,H);
|
|
return OCT_compare(SF,&VD);
|
|
}
|
|
|
|
// update Traffic secret and associated traffic key and IV
|
|
void deriveUpdatedKeys(crypto *context,octad *TS)
|
|
{
|
|
int htype,sha,key;
|
|
char info[16];
|
|
octad INFO = {0,sizeof(info),info};
|
|
char nts[TLS_MAX_HASH];
|
|
octad NTS={0,sizeof(nts),nts};
|
|
|
|
// find cipher suite
|
|
htype=SAL_hashType(context->suite);
|
|
sha=SAL_hashLen(htype);
|
|
|
|
key=SAL_aeadKeylen(context->suite); // depends on key length
|
|
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"traffic upd");
|
|
hkdfExpandLabel(htype,&NTS,sha,TS,&INFO,NULL);
|
|
|
|
OCT_copy(TS,&NTS);
|
|
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"key");
|
|
hkdfExpandLabel(htype,&(context->K),key,TS,&INFO,NULL);
|
|
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"iv");
|
|
hkdfExpandLabel(htype,&(context->IV),12,TS,&INFO,NULL);
|
|
// reset record number
|
|
context->record=0;
|
|
context->active=true;
|
|
}
|
|
|
|
// Create a crypto context from an input raw Secret and an agreed cipher_suite
|
|
void createCryptoContext(int cipher_suite,octad *TS,crypto *context)
|
|
{
|
|
int key,htype=SAL_hashType(cipher_suite);
|
|
|
|
key=SAL_aeadKeylen(cipher_suite);
|
|
|
|
char info[8];
|
|
octad INFO = {0,sizeof(info),info};
|
|
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"key");
|
|
hkdfExpandLabel(htype,&(context->K),key,TS,&INFO,NULL);
|
|
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"iv");
|
|
hkdfExpandLabel(htype,&(context->IV),12,TS,&INFO,NULL);
|
|
|
|
context->active=true;
|
|
context->suite=cipher_suite;
|
|
context->record=0;
|
|
context->taglen=SAL_aeadTaglen(cipher_suite);
|
|
}
|
|
|
|
void createSendCryptoContext(TLS_session *session,octad *TS)
|
|
{
|
|
createCryptoContext(session->cipher_suite,TS,&session->K_send);
|
|
}
|
|
|
|
void createRecvCryptoContext(TLS_session *session,octad *TS)
|
|
{
|
|
createCryptoContext(session->cipher_suite,TS,&session->K_recv);
|
|
}
|
|
|
|
// recover Pre-Shared-Key from Resumption Master Secret
|
|
void recoverPSK(TLS_session *session)
|
|
{
|
|
char info[16];
|
|
octad INFO = {0,sizeof(info),info};
|
|
int htype=SAL_hashType(session->cipher_suite);
|
|
int hlen=SAL_hashLen(htype);
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"resumption");
|
|
hkdfExpandLabel(htype,&session->T.PSK,hlen,&session->RMS, &INFO, &session->T.NONCE);
|
|
}
|
|
|
|
// Key Schedule code
|
|
// Get Early Secret ES and optional Binder Key (either External or Resumption)
|
|
void deriveEarlySecrets(int htype,octad *PSK,octad *ES,octad *BKE,octad *BKR)
|
|
{
|
|
char emh[TLS_MAX_HASH];
|
|
octad EMH = {0,sizeof(emh),emh};
|
|
char zk[TLS_MAX_HASH];
|
|
octad ZK = {0,sizeof(zk),zk};
|
|
char info[16];
|
|
octad INFO = {0,sizeof(info),info};
|
|
|
|
int hlen=SAL_hashLen(htype);
|
|
|
|
OCT_append_byte(&ZK,0,hlen); // Zero key
|
|
|
|
if (PSK==NULL)
|
|
OCT_copy(&EMH,&ZK); // if no PSK available use ZK
|
|
else
|
|
OCT_copy(&EMH,PSK);
|
|
|
|
SAL_hkdfExtract(htype,ES,&ZK,&EMH); // hash function, ES is output, ZK is salt and EMH is IKM
|
|
|
|
SAL_hashNull(htype,&EMH); // EMH = hash of ""
|
|
|
|
if (BKE!=NULL)
|
|
{ // External Binder Key
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"ext binder");
|
|
hkdfExpandLabel(htype,BKE,hlen,ES,&INFO,&EMH);
|
|
}
|
|
if (BKR!=NULL)
|
|
{ // Resumption Binder Key
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"res binder");
|
|
hkdfExpandLabel(htype,BKR,hlen,ES,&INFO,&EMH);
|
|
}
|
|
}
|
|
|
|
// Get Later Secrets (Client Early Traffic Secret CETS and Early Exporter Master Secret EEMS) - requires partial transcript hash H
|
|
void deriveLaterSecrets(int htype,octad *ES,octad *H,octad *CETS,octad *EEMS)
|
|
{
|
|
char info[16];
|
|
octad INFO = {0,sizeof(info),info};
|
|
int hlen=SAL_hashLen(htype);
|
|
|
|
if (CETS!=NULL)
|
|
{
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"c e traffic");
|
|
hkdfExpandLabel(htype,CETS,hlen,ES,&INFO,H);
|
|
}
|
|
if (EEMS!=NULL)
|
|
{
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"e exp master");
|
|
hkdfExpandLabel(htype,EEMS,hlen,ES,&INFO,H);
|
|
}
|
|
}
|
|
|
|
// get Client and Server Handshake secrets for encrypting rest of handshake, from Shared secret SS and early secret ES
|
|
void deriveHandshakeSecrets(TLS_session *session,octad *SS,octad *ES,octad *H)
|
|
{
|
|
char ds[TLS_MAX_HASH];
|
|
octad DS = {0,sizeof(ds),ds}; // Derived Secret
|
|
char emh[TLS_MAX_HASH];
|
|
octad EMH = {0,sizeof(emh),emh}; // Empty Hash
|
|
char info[16];
|
|
octad INFO = {0,sizeof(info),info};
|
|
int htype=SAL_hashType(session->cipher_suite);
|
|
int hlen=SAL_hashLen(htype);
|
|
|
|
SAL_hashNull(htype,&EMH); // hash of ""
|
|
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"derived");
|
|
hkdfExpandLabel(htype,&DS,hlen,ES,&INFO,&EMH);
|
|
|
|
SAL_hkdfExtract(htype,&session->HS,&DS,SS);
|
|
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"c hs traffic");
|
|
hkdfExpandLabel(htype,&session->CTS,hlen,&session->HS,&INFO,H);
|
|
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"s hs traffic");
|
|
hkdfExpandLabel(htype,&session->STS,hlen,&session->HS,&INFO,H);
|
|
}
|
|
|
|
// Extract Client and Server Application Traffic secrets from Transcript Hashes, Handshake secret
|
|
// SFH - Server Finished Hash
|
|
// CFH - Client Finished Hash
|
|
void deriveApplicationSecrets(TLS_session *session,octad *SFH,octad *CFH,octad *EMS)
|
|
{
|
|
char ds[TLS_MAX_HASH];
|
|
octad DS = {0,sizeof(ds),ds};
|
|
char ms[TLS_MAX_HASH];
|
|
octad MS = {0,sizeof(ms),ms};
|
|
char emh[TLS_MAX_HASH];
|
|
octad EMH = {0,sizeof(emh),emh};
|
|
char zk[TLS_MAX_HASH]; // Zero Key
|
|
octad ZK = {0,sizeof(zk),zk};
|
|
char info[16];
|
|
octad INFO = {0,sizeof(info),info};
|
|
int htype=SAL_hashType(session->cipher_suite);
|
|
int hlen=SAL_hashLen(htype);
|
|
|
|
OCT_append_byte(&ZK,0,hlen); // 00..00
|
|
SAL_hashNull(htype,&EMH); // hash("")
|
|
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"derived");
|
|
hkdfExpandLabel(htype,&DS,hlen,&session->HS,&INFO,&EMH); // Use handshake secret from above
|
|
|
|
SAL_hkdfExtract(htype,&MS,&DS,&ZK);
|
|
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"c ap traffic");
|
|
hkdfExpandLabel(htype,&session->CTS,hlen,&MS,&INFO,SFH);
|
|
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"s ap traffic");
|
|
hkdfExpandLabel(htype,&session->STS,hlen,&MS,&INFO,SFH);
|
|
|
|
if (EMS!=NULL)
|
|
{
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"exp master");
|
|
hkdfExpandLabel(htype,EMS,hlen,&MS,&INFO,SFH);
|
|
}
|
|
|
|
OCT_kill(&INFO);
|
|
OCT_append_string(&INFO,(char *)"res master");
|
|
hkdfExpandLabel(htype,&session->RMS,hlen,&MS,&INFO,CFH);
|
|
}
|
|
|
|
// Create Client Cert Verify message, a digital signature using KEY on some TLS1.3 specific message+transcript hash
|
|
void createClientCertVerifier(int sigAlg,octad *H,octad *KEY,octad *CCVSIG)
|
|
{
|
|
char ccv[100+TLS_MAX_HASH];
|
|
octad CCV={0,sizeof(ccv),ccv};
|
|
// create TLS1.3 message to be signed
|
|
OCT_append_byte(&CCV,32,64); // 64 spaces
|
|
OCT_append_string(&CCV,(char *)"TLS 1.3, client CertificateVerify"); // 33 chars
|
|
OCT_append_byte(&CCV,0,1); // add 0 character
|
|
OCT_append_octad(&CCV,H); // add Transcript Hash
|
|
|
|
if (sigAlg==DILITHIUM2_P256)
|
|
{
|
|
octad FKEY={32,32,KEY->val};
|
|
octad SKEY={KEY->len-32,KEY->len-32,&KEY->val[32]};
|
|
SAL_tlsSignature(ECDSA_SECP256R1_SHA384,&FKEY,&CCV,CCVSIG);
|
|
ecdsa_sig_encode(CCVSIG); // ASN.1 encode it - it grows
|
|
octad SSIG={0,TLS_MAX_SIGNATURE_SIZE-32,&CCVSIG->val[CCVSIG->len]};
|
|
SAL_tlsSignature(DILITHIUM2,&SKEY,&CCV,&SSIG); // append PQ sig
|
|
CCVSIG->len += SSIG.len;
|
|
return;
|
|
}
|
|
|
|
SAL_tlsSignature(sigAlg,KEY,&CCV,CCVSIG);
|
|
// adjustment for ECDSA signatures
|
|
if (sigAlg==ECDSA_SECP256R1_SHA256 || sigAlg==ECDSA_SECP384R1_SHA384)
|
|
{
|
|
ecdsa_sig_encode(CCVSIG);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// check that SCVSIG is digital signature (using sigAlg algorithm) of some TLS1.3 specific message+transcript hash,
|
|
// as verified by Server Certificate public key CERTPK
|
|
bool checkServerCertVerifier(int sigAlg,octad *SCVSIG,octad *H,octad *CERTPK)
|
|
{
|
|
// Server Certificate Verify
|
|
char scv[100+TLS_MAX_HASH];
|
|
octad SCV={0,sizeof(scv),scv};
|
|
|
|
// TLS1.3 message that was signed
|
|
OCT_append_byte(&SCV,32,64); // 64 spaces
|
|
OCT_append_string(&SCV,(char *)"TLS 1.3, server CertificateVerify"); // 33 chars
|
|
OCT_append_byte(&SCV,0,1); // add 0 character
|
|
OCT_append_octad(&SCV,H); // add Transcript Hash
|
|
|
|
if (sigAlg==DILITHIUM2_P256)
|
|
{
|
|
octad FPUB={65,65,CERTPK->val};
|
|
octad SPUB={CERTPK->len-65,CERTPK->len-65,&CERTPK->val[65]};
|
|
int len=SCVSIG->len; // full length
|
|
int index=ecdsa_sig_decode(SCVSIG); // ASN.1 decode it - it shrinks - return undecoded length
|
|
if (index==0) return false;
|
|
int mlen=SCVSIG->len; // modified length
|
|
octad FSIG={mlen,mlen,SCVSIG->val};
|
|
octad SSIG={len-index,len-index,&SCVSIG->val[index]};
|
|
return SAL_tlsSignatureVerify(ECDSA_SECP256R1_SHA384,&SCV,&FSIG,&FPUB) && SAL_tlsSignatureVerify(DILITHIUM2,&SCV,&SSIG,&SPUB);
|
|
}
|
|
|
|
// Special case processing required here for ECDSA signatures - SCVSIG is modified
|
|
if (sigAlg==ECDSA_SECP256R1_SHA256 || sigAlg==ECDSA_SECP384R1_SHA384) {
|
|
if (!ecdsa_sig_decode(SCVSIG)) return false;
|
|
}
|
|
log(IO_DEBUG,(char *)"Certificate Signature = ",NULL,0,SCVSIG);
|
|
return SAL_tlsSignatureVerify(sigAlg,&SCV,SCVSIG,CERTPK);
|
|
}
|