libsec: implement server side ECDHE key exchange with secp256r1, move DH state in TlsSec structure, simplify
implement ECDHE key exchange with secp256r1 on the server side, providing perfect forward secrecy (tho slowly). it is easier to just keep the ECDH/DH state in the TlsSec structure, which fits better with the grouping of the functions. we do the cleanup in tlsConnectionFree() now, so a lot of error handling logic could go away. reinvestigated some error paths and removed the ones that cannot error. move functions to fit the logical grouping. combine the code for signing handshake hashes (for client certs) and DH parameters. provide digestDHparams() function to be shared between server and client code.
This commit is contained in:
parent
9ba04fd030
commit
ff47cbd464
|
@ -69,6 +69,14 @@ struct TlsSec {
|
|||
uchar sec[MasterSecretSize]; // master secret
|
||||
uchar crandom[RandomSize]; // client random
|
||||
uchar srandom[RandomSize]; // server random
|
||||
|
||||
// diffie hellman state
|
||||
DHstate dh;
|
||||
struct {
|
||||
ECdomain dom;
|
||||
ECpriv Q;
|
||||
} ec;
|
||||
|
||||
// byte generation and handshake checksum
|
||||
void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int, uchar*, int);
|
||||
void (*setFinished)(TlsSec*, HandshakeHash, uchar*, int);
|
||||
|
@ -86,7 +94,7 @@ typedef struct TlsConnection{
|
|||
int cipher;
|
||||
int nsecret; // amount of secret data to init keys
|
||||
char *digest; // name of digest algorithm to use
|
||||
char *enc; // name of encryption algorithm to use
|
||||
char *enc; // name of encryption algorithm to use
|
||||
|
||||
// for finished messages
|
||||
HandshakeHash handhash;
|
||||
|
@ -345,6 +353,7 @@ static struct {
|
|||
DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*);
|
||||
int len;
|
||||
} hashfun[] = {
|
||||
/* [0x00] is reserved for MD5+SHA1 for < TLS1.2 */
|
||||
[0x01] {md5, MD5dlen},
|
||||
[0x02] {sha1, SHA1dlen},
|
||||
[0x03] {sha2_224, SHA2_224dlen},
|
||||
|
@ -385,29 +394,38 @@ static int setSecrets(TlsConnection *c, int isclient);
|
|||
static int finishedMatch(TlsConnection *c, Finished *f);
|
||||
static void tlsConnectionFree(TlsConnection *c);
|
||||
|
||||
static int isDHE(int tlsid);
|
||||
static int isECDHE(int tlsid);
|
||||
static int isPSK(int tlsid);
|
||||
static int isECDSA(int tlsid);
|
||||
|
||||
static int setAlgs(TlsConnection *c, int a);
|
||||
static int okCipher(Ints *cv, int ispsk);
|
||||
static int okCompression(Bytes *cv);
|
||||
static int initCiphers(void);
|
||||
static Ints* makeciphers(int ispsk);
|
||||
|
||||
static AuthRpc* factotum_rsa_open(RSApub *rsapub);
|
||||
static mpint* factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher);
|
||||
static void factotum_rsa_close(AuthRpc *rpc);
|
||||
|
||||
static void tlsSecInits(TlsSec *sec, int cvers, uchar *crandom);
|
||||
static int tlsSecRSAs(TlsSec *sec, Bytes *epm);
|
||||
static void tlsSecPSKs(TlsSec *sec);
|
||||
static Bytes* tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc);
|
||||
static int tlsSecECDHEs2(TlsSec *sec, Bytes *Yc);
|
||||
static void tlsSecInitc(TlsSec *sec, int cvers);
|
||||
static Bytes* tlsSecRSAc(TlsSec *sec, uchar *cert, int ncert);
|
||||
static void tlsSecPSKc(TlsSec *sec);
|
||||
static Bytes* tlsSecDHEc(TlsSec *sec, Bytes *p, Bytes *g, Bytes *Ys);
|
||||
static Bytes* tlsSecECDHEc(TlsSec *sec, int curve, Bytes *Ys);
|
||||
static void tlsSecVers(TlsSec *sec, int v);
|
||||
static int tlsSecFinished(TlsSec *sec, HandshakeHash hsh, uchar *fin, int nfin, int isclient);
|
||||
static void setMasterSecret(TlsSec *sec, Bytes *pm);
|
||||
static int digestDHparams(TlsSec *sec, Bytes *par, uchar digest[MAXdlen], int sigalg);
|
||||
static char* verifyDHparams(TlsSec *sec, Bytes *par, Bytes *cert, Bytes *sig, int sigalg);
|
||||
|
||||
static Bytes* pkcs1_encrypt(Bytes* data, RSApub* key, int blocktype);
|
||||
static Bytes* pkcs1_decrypt(TlsSec *sec, Bytes *cipher);
|
||||
|
||||
static AuthRpc* factotum_rsa_open(RSApub *rsapub);
|
||||
static mpint* factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher);
|
||||
static void factotum_rsa_close(AuthRpc *rpc);
|
||||
static Bytes* pkcs1_sign(TlsSec *sec, uchar *digest, int digestlen, int sigalg);
|
||||
|
||||
static void* emalloc(int);
|
||||
static void* erealloc(void*, int);
|
||||
|
@ -520,7 +538,7 @@ tlsClientExtensions(TLSconn *conn, int *plen)
|
|||
}
|
||||
|
||||
// ECDHE
|
||||
if(1){
|
||||
if(ProtocolVersion >= TLS10Version){
|
||||
m = p - b;
|
||||
b = erealloc(b, m + 2+2+2+nelem(namedcurves)*2 + 2+2+1+nelem(pointformats));
|
||||
p = b + m;
|
||||
|
@ -655,9 +673,9 @@ tlsServer2(int ctl, int hand,
|
|||
char *pskid, uchar *psk, int psklen,
|
||||
int (*trace)(char*fmt, ...), PEMChain *chp)
|
||||
{
|
||||
int cipher, compressor, numcerts, i;
|
||||
TlsConnection *c;
|
||||
Msg m;
|
||||
int cipher, compressor, numcerts, i;
|
||||
|
||||
if(trace)
|
||||
trace("tlsServer2\n");
|
||||
|
@ -742,6 +760,36 @@ tlsServer2(int ctl, int hand,
|
|||
goto Err;
|
||||
}
|
||||
|
||||
if(isECDHE(cipher)){
|
||||
Namedcurve *nc = &namedcurves[0]; /* secp256r1 */
|
||||
|
||||
m.tag = HServerKeyExchange;
|
||||
m.u.serverKeyExchange.curve = nc->tlsid;
|
||||
m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec, nc);
|
||||
if(m.u.serverKeyExchange.dh_parameters == nil){
|
||||
tlsError(c, EInternalError, "can't set DH parameters");
|
||||
goto Err;
|
||||
}
|
||||
|
||||
/* sign the DH parameters */
|
||||
if(certlen > 0){
|
||||
uchar digest[MAXdlen];
|
||||
int digestlen;
|
||||
|
||||
if(c->version >= TLS12Version)
|
||||
m.u.serverKeyExchange.sigalg = 0x0401; /* RSA SHA256 */
|
||||
digestlen = digestDHparams(c->sec, m.u.serverKeyExchange.dh_parameters,
|
||||
digest, m.u.serverKeyExchange.sigalg);
|
||||
if((m.u.serverKeyExchange.dh_signature = pkcs1_sign(c->sec, digest, digestlen,
|
||||
m.u.serverKeyExchange.sigalg)) == nil){
|
||||
tlsError(c, EHandshakeFailure, "pkcs1_sign: %r");
|
||||
goto Err;
|
||||
}
|
||||
}
|
||||
if(!msgSend(c, &m, AQueue))
|
||||
goto Err;
|
||||
}
|
||||
|
||||
m.tag = HServerHelloDone;
|
||||
if(!msgSend(c, &m, AFlush))
|
||||
goto Err;
|
||||
|
@ -760,13 +808,18 @@ tlsServer2(int ctl, int hand,
|
|||
goto Err;
|
||||
}
|
||||
}
|
||||
if(certlen > 0){
|
||||
if(isECDHE(cipher)){
|
||||
if(tlsSecECDHEs2(c->sec, m.u.clientKeyExchange.key) < 0){
|
||||
tlsError(c, EHandshakeFailure, "couldn't set keys: %r");
|
||||
goto Err;
|
||||
}
|
||||
} else if(certlen > 0){
|
||||
if(tlsSecRSAs(c->sec, m.u.clientKeyExchange.key) < 0){
|
||||
tlsError(c, EHandshakeFailure, "couldn't set keys: %r");
|
||||
goto Err;
|
||||
}
|
||||
} else if(psklen > 0){
|
||||
tlsSecPSKs(c->sec);
|
||||
setMasterSecret(c->sec, newbytes(psklen));
|
||||
} else {
|
||||
tlsError(c, EInternalError, "no psk or certificate");
|
||||
goto Err;
|
||||
|
@ -823,79 +876,29 @@ Err:
|
|||
return nil;
|
||||
}
|
||||
|
||||
static int
|
||||
isDHE(int tlsid)
|
||||
{
|
||||
switch(tlsid){
|
||||
case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
|
||||
case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
|
||||
case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
|
||||
case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
|
||||
case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
|
||||
case TLS_DHE_RSA_WITH_CHACHA20_POLY1305:
|
||||
case GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
isECDHE(int tlsid)
|
||||
{
|
||||
switch(tlsid){
|
||||
case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
|
||||
case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305:
|
||||
|
||||
case GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
|
||||
case GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305:
|
||||
|
||||
case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
|
||||
case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
|
||||
|
||||
case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
|
||||
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
|
||||
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
|
||||
case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
isPSK(int tlsid)
|
||||
{
|
||||
switch(tlsid){
|
||||
case TLS_PSK_WITH_CHACHA20_POLY1305:
|
||||
case TLS_PSK_WITH_AES_128_CBC_SHA256:
|
||||
case TLS_PSK_WITH_AES_128_CBC_SHA:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Bytes*
|
||||
tlsSecDHEc(TlsSec *sec, Bytes *p, Bytes *g, Bytes *Ys)
|
||||
{
|
||||
DHstate *dh = &sec->dh;
|
||||
mpint *G, *P, *Y, *K;
|
||||
Bytes *epm;
|
||||
DHstate dh;
|
||||
Bytes *Yc;
|
||||
|
||||
if(p == nil || g == nil || Ys == nil)
|
||||
return nil;
|
||||
|
||||
epm = nil;
|
||||
Yc = nil;
|
||||
P = bytestomp(p);
|
||||
G = bytestomp(g);
|
||||
Y = bytestomp(Ys);
|
||||
K = nil;
|
||||
|
||||
if(P == nil || G == nil || Y == nil || dh_new(&dh, P, nil, G) == nil)
|
||||
if(dh_new(dh, P, nil, G) == nil)
|
||||
goto Out;
|
||||
epm = mptobytes(dh.y);
|
||||
K = dh_finish(&dh, Y);
|
||||
Yc = mptobytes(dh->y);
|
||||
K = dh_finish(dh, Y); /* zeros dh */
|
||||
if(K == nil){
|
||||
freebytes(epm);
|
||||
epm = nil;
|
||||
freebytes(Yc);
|
||||
Yc = nil;
|
||||
goto Out;
|
||||
}
|
||||
setMasterSecret(sec, mptobytes(K));
|
||||
|
@ -906,132 +909,53 @@ Out:
|
|||
mpfree(G);
|
||||
mpfree(P);
|
||||
|
||||
return epm;
|
||||
return Yc;
|
||||
}
|
||||
|
||||
static Bytes*
|
||||
tlsSecECDHEc(TlsSec *sec, int curve, Bytes *Ys)
|
||||
{
|
||||
Namedcurve *nc, *enc;
|
||||
Bytes *epm;
|
||||
ECdomain dom;
|
||||
ECdomain *dom = &sec->ec.dom;
|
||||
ECpriv *Q = &sec->ec.Q;
|
||||
Namedcurve *nc;
|
||||
ECpub *pub;
|
||||
ECpoint K;
|
||||
ECpriv Q;
|
||||
Bytes *Yc;
|
||||
|
||||
if(Ys == nil)
|
||||
return nil;
|
||||
|
||||
enc = &namedcurves[nelem(namedcurves)];
|
||||
for(nc = namedcurves; nc != enc; nc++)
|
||||
for(nc = namedcurves; nc != &namedcurves[nelem(namedcurves)]; nc++)
|
||||
if(nc->tlsid == curve)
|
||||
break;
|
||||
goto Found;
|
||||
return nil;
|
||||
|
||||
if(nc == enc)
|
||||
Found:
|
||||
ecdominit(dom, nc->init);
|
||||
pub = ecdecodepub(dom, Ys->data, Ys->len);
|
||||
if(pub == nil)
|
||||
return nil;
|
||||
|
||||
ecdominit(&dom, nc->init);
|
||||
pub = ecdecodepub(&dom, Ys->data, Ys->len);
|
||||
if(pub == nil){
|
||||
ecdomfree(&dom);
|
||||
return nil;
|
||||
}
|
||||
|
||||
memset(&Q, 0, sizeof(Q));
|
||||
Q.x = mpnew(0);
|
||||
Q.y = mpnew(0);
|
||||
Q.d = mpnew(0);
|
||||
memset(Q, 0, sizeof(*Q));
|
||||
Q->x = mpnew(0);
|
||||
Q->y = mpnew(0);
|
||||
Q->d = mpnew(0);
|
||||
|
||||
memset(&K, 0, sizeof(K));
|
||||
K.x = mpnew(0);
|
||||
K.y = mpnew(0);
|
||||
|
||||
epm = nil;
|
||||
if(ecgen(&dom, &Q) != nil){
|
||||
ecmul(&dom, pub, Q.d, &K);
|
||||
setMasterSecret(sec, mptobytes(K.x));
|
||||
epm = newbytes(1 + 2*((mpsignif(dom.p)+7)/8));
|
||||
epm->len = ecencodepub(&dom, &Q, epm->data, epm->len);
|
||||
}
|
||||
ecgen(dom, Q);
|
||||
ecmul(dom, pub, Q->d, &K);
|
||||
setMasterSecret(sec, mptobytes(K.x));
|
||||
Yc = newbytes(1 + 2*((mpsignif(dom->p)+7)/8));
|
||||
Yc->len = ecencodepub(dom, Q, Yc->data, Yc->len);
|
||||
|
||||
mpfree(K.x);
|
||||
mpfree(K.y);
|
||||
mpfree(Q.x);
|
||||
mpfree(Q.y);
|
||||
mpfree(Q.d);
|
||||
|
||||
ecpubfree(pub);
|
||||
ecdomfree(&dom);
|
||||
|
||||
return epm;
|
||||
}
|
||||
|
||||
static char*
|
||||
verifyDHparams(TlsConnection *c, Bytes *par, Bytes *sig, int sigalg)
|
||||
{
|
||||
uchar digest[MAXdlen];
|
||||
int digestlen;
|
||||
ECdomain dom;
|
||||
ECpub *ecpk;
|
||||
RSApub *rsapk;
|
||||
Bytes *blob;
|
||||
char *err;
|
||||
|
||||
if(par == nil || par->len <= 0)
|
||||
return "no DH parameters";
|
||||
|
||||
if(sig == nil || sig->len <= 0){
|
||||
if(c->sec->psklen > 0)
|
||||
return nil;
|
||||
return "no signature";
|
||||
}
|
||||
|
||||
if(c->cert == nil)
|
||||
return "no certificate";
|
||||
|
||||
blob = newbytes(2*RandomSize + par->len);
|
||||
memmove(blob->data+0*RandomSize, c->sec->crandom, RandomSize);
|
||||
memmove(blob->data+1*RandomSize, c->sec->srandom, RandomSize);
|
||||
memmove(blob->data+2*RandomSize, par->data, par->len);
|
||||
if(c->version < TLS12Version){
|
||||
digestlen = MD5dlen + SHA1dlen;
|
||||
md5(blob->data, blob->len, digest, nil);
|
||||
sha1(blob->data, blob->len, digest+MD5dlen, nil);
|
||||
sigalg = 1; // only RSA signatures supported for version <= TLS1.1
|
||||
} else {
|
||||
int hashalg = (sigalg>>8) & 0xFF;
|
||||
digestlen = -1;
|
||||
if(hashalg < nelem(hashfun) && hashfun[hashalg].fun != nil){
|
||||
digestlen = hashfun[hashalg].len;
|
||||
(*hashfun[hashalg].fun)(blob->data, blob->len, digest, nil);
|
||||
}
|
||||
}
|
||||
freebytes(blob);
|
||||
|
||||
if(digestlen <= 0)
|
||||
return "unknown signature digest algorithm";
|
||||
|
||||
switch(sigalg & 0xFF){
|
||||
case 0x01:
|
||||
rsapk = X509toRSApub(c->cert->data, c->cert->len, nil, 0);
|
||||
if(rsapk == nil)
|
||||
return "bad certificate";
|
||||
err = X509rsaverifydigest(sig->data, sig->len, digest, digestlen, rsapk);
|
||||
rsapubfree(rsapk);
|
||||
break;
|
||||
case 0x03:
|
||||
ecpk = X509toECpub(c->cert->data, c->cert->len, &dom);
|
||||
if(ecpk == nil)
|
||||
return "bad certificate";
|
||||
err = X509ecdsaverifydigest(sig->data, sig->len, digest, digestlen, &dom, ecpk);
|
||||
ecdomfree(&dom);
|
||||
ecpubfree(ecpk);
|
||||
break;
|
||||
default:
|
||||
err = "signaure algorithm not RSA or ECDSA";
|
||||
}
|
||||
|
||||
return err;
|
||||
return Yc;
|
||||
}
|
||||
|
||||
static TlsConnection *
|
||||
|
@ -1041,10 +965,10 @@ tlsClient2(int ctl, int hand,
|
|||
uchar *ext, int extlen,
|
||||
int (*trace)(char*fmt, ...))
|
||||
{
|
||||
TlsConnection *c;
|
||||
Msg m;
|
||||
int creq, dhx, cipher;
|
||||
TlsConnection *c;
|
||||
Bytes *epm;
|
||||
Msg m;
|
||||
|
||||
if(!initCiphers())
|
||||
return nil;
|
||||
|
@ -1130,10 +1054,11 @@ tlsClient2(int ctl, int hand,
|
|||
}
|
||||
if(m.tag == HServerKeyExchange) {
|
||||
if(dhx){
|
||||
char *err = verifyDHparams(c,
|
||||
char *err = verifyDHparams(c->sec,
|
||||
m.u.serverKeyExchange.dh_parameters,
|
||||
c->cert,
|
||||
m.u.serverKeyExchange.dh_signature,
|
||||
m.u.serverKeyExchange.sigalg);
|
||||
c->version<TLS12Version ? 0x01 : m.u.serverKeyExchange.sigalg);
|
||||
if(err != nil){
|
||||
tlsError(c, EBadCertificate, "can't verify DH parameters: %s", err);
|
||||
goto Err;
|
||||
|
@ -1184,7 +1109,7 @@ tlsClient2(int ctl, int hand,
|
|||
goto Err;
|
||||
}
|
||||
} else if(psklen > 0){
|
||||
tlsSecPSKc(c->sec);
|
||||
setMasterSecret(c->sec, newbytes(psklen));
|
||||
} else {
|
||||
tlsError(c, EInternalError, "no psk or certificate");
|
||||
goto Err;
|
||||
|
@ -1224,40 +1149,29 @@ tlsClient2(int ctl, int hand,
|
|||
|
||||
/* certificate verify */
|
||||
if(creq && certlen > 0) {
|
||||
mpint *signedMP, *paddedHashes;
|
||||
HandshakeHash hsave;
|
||||
uchar buf[512];
|
||||
int buflen;
|
||||
uchar digest[MAXdlen];
|
||||
int digestlen;
|
||||
|
||||
/* save the state for the Finish message */
|
||||
hsave = c->handhash;
|
||||
if(c->version >= TLS12Version){
|
||||
uchar digest[SHA2_256dlen];
|
||||
|
||||
if(c->version < TLS12Version){
|
||||
md5(nil, 0, digest, &c->handhash.md5);
|
||||
sha1(nil, 0, digest+MD5dlen, &c->handhash.sha1);
|
||||
digestlen = MD5dlen+SHA1dlen;
|
||||
} else {
|
||||
m.u.certificateVerify.sigalg = 0x0401; /* RSA SHA256 */
|
||||
sha2_256(nil, 0, digest, &c->handhash.sha2_256);
|
||||
buflen = asn1encodedigest(sha2_256, digest, buf, sizeof(buf));
|
||||
} else {
|
||||
md5(nil, 0, buf, &c->handhash.md5);
|
||||
sha1(nil, 0, buf+MD5dlen, &c->handhash.sha1);
|
||||
buflen = MD5dlen+SHA1dlen;
|
||||
digestlen = SHA2_256dlen;
|
||||
}
|
||||
c->handhash = hsave;
|
||||
|
||||
if(buflen <= 0){
|
||||
tlsError(c, EInternalError, "can't encode handshake hashes");
|
||||
if((m.u.certificateVerify.signature = pkcs1_sign(c->sec, digest, digestlen,
|
||||
m.u.certificateVerify.sigalg)) == nil){
|
||||
tlsError(c, EHandshakeFailure, "pkcs1_sign: %r");
|
||||
goto Err;
|
||||
}
|
||||
|
||||
paddedHashes = pkcs1padbuf(buf, buflen, c->sec->rsapub->n);
|
||||
signedMP = factotum_rsa_decrypt(c->sec->rpc, paddedHashes);
|
||||
if(signedMP == nil){
|
||||
tlsError(c, EHandshakeFailure, "factotum_rsa_decrypt: %r");
|
||||
goto Err;
|
||||
}
|
||||
m.u.certificateVerify.signature = mptobytes(signedMP);
|
||||
mpfree(signedMP);
|
||||
|
||||
m.tag = HCertificateVerify;
|
||||
if(!msgSend(c, &m, AFlush))
|
||||
goto Err;
|
||||
|
@ -1440,6 +1354,30 @@ msgSend(TlsConnection *c, Msg *m, int act)
|
|||
memmove(p, m->u.certificateVerify.signature->data, m->u.certificateVerify.signature->len);
|
||||
p += m->u.certificateVerify.signature->len;
|
||||
break;
|
||||
case HServerKeyExchange:
|
||||
if(m->u.serverKeyExchange.pskid != nil){
|
||||
n = m->u.serverKeyExchange.pskid->len;
|
||||
put16(p, n);
|
||||
p += 2;
|
||||
memmove(p, m->u.serverKeyExchange.pskid->data, n);
|
||||
p += n;
|
||||
}
|
||||
if(m->u.serverKeyExchange.dh_parameters == nil)
|
||||
break;
|
||||
n = m->u.serverKeyExchange.dh_parameters->len;
|
||||
memmove(p, m->u.serverKeyExchange.dh_parameters->data, n);
|
||||
p += n;
|
||||
if(m->u.serverKeyExchange.dh_signature == nil)
|
||||
break;
|
||||
if(c->version >= TLS12Version){
|
||||
put16(p, m->u.serverKeyExchange.sigalg);
|
||||
p += 2;
|
||||
}
|
||||
n = m->u.serverKeyExchange.dh_signature->len;
|
||||
put16(p, n), p += 2;
|
||||
memmove(p, m->u.serverKeyExchange.dh_signature->data, n);
|
||||
p += n;
|
||||
break;
|
||||
case HClientKeyExchange:
|
||||
if(m->u.clientKeyExchange.pskid != nil){
|
||||
n = m->u.clientKeyExchange.pskid->len;
|
||||
|
@ -1823,10 +1761,6 @@ msgRecv(TlsConnection *c, Msg *m)
|
|||
}
|
||||
break;
|
||||
case HClientKeyExchange:
|
||||
/*
|
||||
* this message depends upon the encryption selected
|
||||
* assume rsa.
|
||||
*/
|
||||
if(isPSK(c->cipher)){
|
||||
if(n < 2)
|
||||
goto Short;
|
||||
|
@ -1844,8 +1778,12 @@ msgRecv(TlsConnection *c, Msg *m)
|
|||
else{
|
||||
if(n < 2)
|
||||
goto Short;
|
||||
nn = get16(p);
|
||||
p += 2, n -= 2;
|
||||
if(isECDHE(c->cipher))
|
||||
nn = *p++, n--;
|
||||
else {
|
||||
nn = get16(p);
|
||||
p += 2, n -= 2;
|
||||
}
|
||||
}
|
||||
if(n < nn)
|
||||
goto Short;
|
||||
|
@ -1941,11 +1879,11 @@ bytesPrint(char *bs, char *be, char *s0, Bytes *b, char *s1)
|
|||
if(b == nil)
|
||||
bs = seprint(bs, be, "nil");
|
||||
else {
|
||||
bs = seprint(bs, be, "<%d> [", b->len);
|
||||
bs = seprint(bs, be, "<%d> [ ", b->len);
|
||||
for(i=0; i<b->len; i++)
|
||||
bs = seprint(bs, be, "%.2x ", b->data[i]);
|
||||
bs = seprint(bs, be, "]");
|
||||
}
|
||||
bs = seprint(bs, be, "]");
|
||||
if(s1)
|
||||
bs = seprint(bs, be, "%s", s1);
|
||||
return bs;
|
||||
|
@ -1958,13 +1896,14 @@ intsPrint(char *bs, char *be, char *s0, Ints *b, char *s1)
|
|||
|
||||
if(s0)
|
||||
bs = seprint(bs, be, "%s", s0);
|
||||
bs = seprint(bs, be, "[");
|
||||
if(b == nil)
|
||||
bs = seprint(bs, be, "nil");
|
||||
else
|
||||
else {
|
||||
bs = seprint(bs, be, "[ ");
|
||||
for(i=0; i<b->len; i++)
|
||||
bs = seprint(bs, be, "%x ", b->data[i]);
|
||||
bs = seprint(bs, be, "]");
|
||||
bs = seprint(bs, be, "]");
|
||||
}
|
||||
if(s1)
|
||||
bs = seprint(bs, be, "%s", s1);
|
||||
return bs;
|
||||
|
@ -2116,9 +2055,18 @@ tlsConnectionFree(TlsConnection *c)
|
|||
{
|
||||
if(c == nil)
|
||||
return;
|
||||
|
||||
dh_finish(&c->sec->dh, nil);
|
||||
|
||||
mpfree(c->sec->ec.Q.x);
|
||||
mpfree(c->sec->ec.Q.y);
|
||||
mpfree(c->sec->ec.Q.d);
|
||||
ecdomfree(&c->sec->ec.dom);
|
||||
|
||||
factotum_rsa_close(c->sec->rpc);
|
||||
rsapubfree(c->sec->rsapub);
|
||||
freebytes(c->cert);
|
||||
|
||||
memset(c, 0, sizeof(*c));
|
||||
free(c);
|
||||
}
|
||||
|
@ -2126,6 +2074,69 @@ tlsConnectionFree(TlsConnection *c)
|
|||
|
||||
//================= cipher choices ========================
|
||||
|
||||
static int
|
||||
isDHE(int tlsid)
|
||||
{
|
||||
switch(tlsid){
|
||||
case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
|
||||
case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
|
||||
case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
|
||||
case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
|
||||
case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
|
||||
case TLS_DHE_RSA_WITH_CHACHA20_POLY1305:
|
||||
case GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
isECDHE(int tlsid)
|
||||
{
|
||||
switch(tlsid){
|
||||
case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
|
||||
case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305:
|
||||
|
||||
case GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
|
||||
case GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305:
|
||||
|
||||
case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
|
||||
case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
|
||||
|
||||
case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
|
||||
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
|
||||
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
|
||||
case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
isPSK(int tlsid)
|
||||
{
|
||||
switch(tlsid){
|
||||
case TLS_PSK_WITH_CHACHA20_POLY1305:
|
||||
case TLS_PSK_WITH_AES_128_CBC_SHA256:
|
||||
case TLS_PSK_WITH_AES_128_CBC_SHA:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
isECDSA(int tlsid)
|
||||
{
|
||||
switch(tlsid){
|
||||
case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
|
||||
case GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
|
||||
case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
|
||||
case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
setAlgs(TlsConnection *c, int a)
|
||||
{
|
||||
|
@ -2152,8 +2163,8 @@ okCipher(Ints *cv, int ispsk)
|
|||
|
||||
for(i = 0; i < cv->len; i++) {
|
||||
c = cv->data[i];
|
||||
if(isDHE(c) || isECDHE(c) || isPSK(c) != ispsk)
|
||||
continue; /* TODO: not implemented for server */
|
||||
if(isECDSA(c) || isDHE(c) || isPSK(c) != ispsk)
|
||||
continue; /* not implemented for server */
|
||||
for(j = 0; j < nelem(cipherAlgs); j++)
|
||||
if(cipherAlgs[j].ok && cipherAlgs[j].tlsid == c)
|
||||
return c;
|
||||
|
@ -2261,7 +2272,6 @@ makeciphers(int ispsk)
|
|||
}
|
||||
|
||||
|
||||
|
||||
//================= security functions ========================
|
||||
|
||||
// given a public key, set up connection to factotum
|
||||
|
@ -2528,8 +2538,6 @@ tls12SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient)
|
|||
p_sha256(finished, TLSFinishedLen, sec->sec, MasterSecretSize, (uchar*)label, strlen(label), seed, SHA2_256dlen);
|
||||
}
|
||||
|
||||
/* the keys are verified to have the same public components
|
||||
* and to function correctly with pkcs 1 encryption and decryption. */
|
||||
static void
|
||||
tlsSecInits(TlsSec *sec, int cvers, uchar *crandom)
|
||||
{
|
||||
|
@ -2562,10 +2570,62 @@ tlsSecRSAs(TlsSec *sec, Bytes *epm)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
tlsSecPSKs(TlsSec *sec)
|
||||
static Bytes*
|
||||
tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc)
|
||||
{
|
||||
setMasterSecret(sec, newbytes(sec->psklen));
|
||||
ECdomain *dom = &sec->ec.dom;
|
||||
ECpriv *Q = &sec->ec.Q;
|
||||
Bytes *par;
|
||||
int n;
|
||||
|
||||
ecdominit(dom, nc->init);
|
||||
memset(Q, 0, sizeof(*Q));
|
||||
Q->x = mpnew(0);
|
||||
Q->y = mpnew(0);
|
||||
Q->d = mpnew(0);
|
||||
ecgen(dom, Q);
|
||||
n = 1 + 2*((mpsignif(dom->p)+7)/8);
|
||||
par = newbytes(1+2+1+n);
|
||||
par->data[0] = 3;
|
||||
put16(par->data+1, nc->tlsid);
|
||||
n = ecencodepub(dom, Q, par->data+4, par->len-4);
|
||||
par->data[3] = n;
|
||||
par->len = 1+2+1+n;
|
||||
|
||||
return par;
|
||||
}
|
||||
|
||||
static int
|
||||
tlsSecECDHEs2(TlsSec *sec, Bytes *Yc)
|
||||
{
|
||||
ECdomain *dom = &sec->ec.dom;
|
||||
ECpriv *Q = &sec->ec.Q;
|
||||
ECpoint K;
|
||||
ECpub *Y;
|
||||
|
||||
if(Yc == nil){
|
||||
werrstr("no public key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){
|
||||
werrstr("bad public key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&K, 0, sizeof(K));
|
||||
K.x = mpnew(0);
|
||||
K.y = mpnew(0);
|
||||
|
||||
ecmul(dom, Y, Q->d, &K);
|
||||
setMasterSecret(sec, mptobytes(K.x));
|
||||
|
||||
mpfree(K.x);
|
||||
mpfree(K.y);
|
||||
|
||||
ecpubfree(Y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -2577,12 +2637,6 @@ tlsSecInitc(TlsSec *sec, int cvers)
|
|||
genrandom(sec->crandom+4, RandomSize-4);
|
||||
}
|
||||
|
||||
static void
|
||||
tlsSecPSKc(TlsSec *sec)
|
||||
{
|
||||
setMasterSecret(sec, newbytes(sec->psklen));
|
||||
}
|
||||
|
||||
static Bytes*
|
||||
tlsSecRSAc(TlsSec *sec, uchar *cert, int ncert)
|
||||
{
|
||||
|
@ -2695,28 +2749,82 @@ setMasterSecret(TlsSec *sec, Bytes *pm)
|
|||
freebytes(pm);
|
||||
}
|
||||
|
||||
static mpint*
|
||||
bytestomp(Bytes* bytes)
|
||||
static int
|
||||
digestDHparams(TlsSec *sec, Bytes *par, uchar digest[MAXdlen], int sigalg)
|
||||
{
|
||||
return betomp(bytes->data, bytes->len, nil);
|
||||
int hashalg = (sigalg>>8) & 0xFF;
|
||||
int digestlen;
|
||||
Bytes *blob;
|
||||
|
||||
blob = newbytes(2*RandomSize + par->len);
|
||||
memmove(blob->data+0*RandomSize, sec->crandom, RandomSize);
|
||||
memmove(blob->data+1*RandomSize, sec->srandom, RandomSize);
|
||||
memmove(blob->data+2*RandomSize, par->data, par->len);
|
||||
if(hashalg == 0){
|
||||
digestlen = MD5dlen+SHA1dlen;
|
||||
md5(blob->data, blob->len, digest, nil);
|
||||
sha1(blob->data, blob->len, digest+MD5dlen, nil);
|
||||
} else {
|
||||
digestlen = -1;
|
||||
if(hashalg < nelem(hashfun) && hashfun[hashalg].fun != nil){
|
||||
digestlen = hashfun[hashalg].len;
|
||||
(*hashfun[hashalg].fun)(blob->data, blob->len, digest, nil);
|
||||
}
|
||||
}
|
||||
freebytes(blob);
|
||||
return digestlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert mpint* to Bytes, putting high order byte first.
|
||||
*/
|
||||
static Bytes*
|
||||
mptobytes(mpint* big)
|
||||
static char*
|
||||
verifyDHparams(TlsSec *sec, Bytes *par, Bytes *cert, Bytes *sig, int sigalg)
|
||||
{
|
||||
Bytes* ans;
|
||||
int n;
|
||||
uchar digest[MAXdlen];
|
||||
int digestlen;
|
||||
ECdomain dom;
|
||||
ECpub *ecpk;
|
||||
RSApub *rsapk;
|
||||
char *err;
|
||||
|
||||
n = (mpsignif(big)+7)/8;
|
||||
if(n == 0) n = 1;
|
||||
ans = newbytes(n);
|
||||
mptober(big, ans->data, ans->len);
|
||||
return ans;
|
||||
if(par == nil || par->len <= 0)
|
||||
return "no DH parameters";
|
||||
|
||||
if(sig == nil || sig->len <= 0){
|
||||
if(sec->psklen > 0)
|
||||
return nil;
|
||||
return "no signature";
|
||||
}
|
||||
|
||||
if(cert == nil)
|
||||
return "no certificate";
|
||||
|
||||
digestlen = digestDHparams(sec, par, digest, sigalg);
|
||||
if(digestlen <= 0)
|
||||
return "unknown signature digest algorithm";
|
||||
|
||||
switch(sigalg & 0xFF){
|
||||
case 0x01:
|
||||
rsapk = X509toRSApub(cert->data, cert->len, nil, 0);
|
||||
if(rsapk == nil)
|
||||
return "bad certificate";
|
||||
err = X509rsaverifydigest(sig->data, sig->len, digest, digestlen, rsapk);
|
||||
rsapubfree(rsapk);
|
||||
break;
|
||||
case 0x03:
|
||||
ecpk = X509toECpub(cert->data, cert->len, &dom);
|
||||
if(ecpk == nil)
|
||||
return "bad certificate";
|
||||
err = X509ecdsaverifydigest(sig->data, sig->len, digest, digestlen, &dom, ecpk);
|
||||
ecdomfree(&dom);
|
||||
ecpubfree(ecpk);
|
||||
break;
|
||||
default:
|
||||
err = "signaure algorithm not RSA or ECDSA";
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
// Do RSA computation on block according to key, and pad
|
||||
// result on left with zeros to make it modlen long.
|
||||
static Bytes*
|
||||
|
@ -2817,6 +2925,32 @@ pkcs1_decrypt(TlsSec *sec, Bytes *cipher)
|
|||
return nil;
|
||||
}
|
||||
|
||||
static Bytes*
|
||||
pkcs1_sign(TlsSec *sec, uchar *digest, int digestlen, int sigalg)
|
||||
{
|
||||
int hashalg = (sigalg>>8)&0xFF;
|
||||
mpint *signedMP;
|
||||
Bytes *signature;
|
||||
uchar buf[128];
|
||||
|
||||
if(hashalg > 0 && hashalg < nelem(hashfun) && hashfun[hashalg].len == digestlen)
|
||||
digestlen = asn1encodedigest(hashfun[hashalg].fun, digest, buf, sizeof(buf));
|
||||
else if(digestlen == MD5dlen+SHA1dlen)
|
||||
memmove(buf, digest, digestlen);
|
||||
else
|
||||
digestlen = -1;
|
||||
if(digestlen <= 0){
|
||||
werrstr("bad digest algorithm");
|
||||
return nil;
|
||||
}
|
||||
signedMP = factotum_rsa_decrypt(sec->rpc, pkcs1padbuf(buf, digestlen, sec->rsapub->n));
|
||||
if(signedMP == nil)
|
||||
return nil;
|
||||
signature = mptobytes(signedMP);
|
||||
mpfree(signedMP);
|
||||
return signature;
|
||||
}
|
||||
|
||||
|
||||
//================= general utility functions ========================
|
||||
|
||||
|
@ -2920,6 +3054,28 @@ freebytes(Bytes* b)
|
|||
free(b);
|
||||
}
|
||||
|
||||
static mpint*
|
||||
bytestomp(Bytes* bytes)
|
||||
{
|
||||
return betomp(bytes->data, bytes->len, nil);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert mpint* to Bytes, putting high order byte first.
|
||||
*/
|
||||
static Bytes*
|
||||
mptobytes(mpint* big)
|
||||
{
|
||||
Bytes* ans;
|
||||
int n;
|
||||
|
||||
n = (mpsignif(big)+7)/8;
|
||||
if(n == 0) n = 1;
|
||||
ans = newbytes(n);
|
||||
mptober(big, ans->data, ans->len);
|
||||
return ans;
|
||||
}
|
||||
|
||||
/* len is number of ints */
|
||||
static Ints*
|
||||
newints(int len)
|
||||
|
|
Loading…
Reference in a new issue