libsec: implement dh parameter signature verification, stop lying about non-rsa ciphers, fix memory leaks in X509 code
actually verify the diffie hellman parameter signature, this comes in two flavours. TLS1.2 uses X509 signature with a single hash specified by the signature algorithm field in the signature itself and pre TLS1.2 where md5+sha1 hashes of the signed blob are pkcs1 padded and encrypted with the rsa private key. stop advertizing non-rsa cipher suits (DSS and ECDSA), as we have not implmenented them. fix some memory leaks in X509 code while we'r at it.
This commit is contained in:
parent
8baa859319
commit
c3e1c158f6
3 changed files with 148 additions and 66 deletions
|
@ -274,6 +274,8 @@ uchar* decodePEM(char *s, char *type, int *len, char **new_s);
|
|||
PEMChain* decodepemchain(char *s, char *type);
|
||||
uchar* X509gen(RSApriv *priv, char *subj, ulong valid[2], int *certlen);
|
||||
uchar* X509req(RSApriv *priv, char *subj, int *certlen);
|
||||
char* X509verifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, RSApub *pk);
|
||||
char* X509verifydata(uchar *sig, int siglen, uchar *data, int datalen, RSApub *pk);
|
||||
char* X509verify(uchar *cert, int ncert, RSApub *pk);
|
||||
void X509dump(uchar *cert, int ncert);
|
||||
|
||||
|
|
|
@ -140,6 +140,7 @@ typedef struct Msg{
|
|||
Bytes *dh_p;
|
||||
Bytes *dh_g;
|
||||
Bytes *dh_Ys;
|
||||
Bytes *dh_parameters;
|
||||
Bytes *dh_signature;
|
||||
int sigalg;
|
||||
int curve;
|
||||
|
@ -267,11 +268,9 @@ enum {
|
|||
TLS_DH_anon_WITH_AES_256_CBC_SHA = 0X003A,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256 = 0X003C,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA256 = 0X003D,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0XC009,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0XC00A,
|
||||
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0X0067,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0XC013,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0XC014,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0XC023,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
|
||||
};
|
||||
|
||||
|
@ -282,12 +281,10 @@ enum {
|
|||
};
|
||||
|
||||
static Algs cipherAlgs[] = {
|
||||
{"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
|
||||
{"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
|
||||
{"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
|
||||
{"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},
|
||||
{"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
|
||||
{"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
|
||||
{"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_DHE_RSA_WITH_AES_128_CBC_SHA256},
|
||||
{"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_DHE_RSA_WITH_AES_128_CBC_SHA},
|
||||
{"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
|
||||
{"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_RSA_WITH_AES_128_CBC_SHA256},
|
||||
|
@ -318,16 +315,12 @@ static uchar pointformats[] = {
|
|||
CompressionNull /* support of uncompressed point format is mandatory */
|
||||
};
|
||||
|
||||
// signature algorithms
|
||||
// signature algorithms (only RSA at the moment)
|
||||
static int sigalgs[] = {
|
||||
0x0601, /* SHA512 RSA */
|
||||
0x0501, /* SHA384 RSA */
|
||||
0x0401, /* SHA256 RSA */
|
||||
0x0201, /* SHA1 RSA */
|
||||
0x0603, /* SHA512 ECDSA */
|
||||
0x0503, /* SHA384 ECDSA */
|
||||
0x0403, /* SHA256 ECDSA */
|
||||
0x0203, /* SHA1 ECDSA */
|
||||
};
|
||||
|
||||
static TlsConnection *tlsServer2(int ctl, int hand, uchar *cert, int certlen, int (*trace)(char*fmt, ...), PEMChain *chain);
|
||||
|
@ -392,7 +385,8 @@ static Ints* newints(int len);
|
|||
static void freeints(Ints* b);
|
||||
|
||||
/* x509.c */
|
||||
extern mpint* pkcs1padbuf(uchar *buf, int len, mpint *modulus);
|
||||
extern mpint* pkcs1padbuf(uchar *buf, int len, mpint *modulus);
|
||||
extern int pkcs1decryptsignature(uchar *sig, int siglen, RSApub *pk, uchar **pbuf);
|
||||
|
||||
//================= client/server ========================
|
||||
|
||||
|
@ -783,16 +777,10 @@ static int
|
|||
isDHE(int tlsid)
|
||||
{
|
||||
switch(tlsid){
|
||||
case TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA:
|
||||
case TLS_DHE_DSS_WITH_DES_CBC_SHA:
|
||||
case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
|
||||
case TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA:
|
||||
case TLS_DHE_RSA_WITH_DES_CBC_SHA:
|
||||
case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
|
||||
case TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
|
||||
case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
|
||||
case TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
|
||||
case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
|
||||
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:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -805,9 +793,6 @@ isECDHE(int tlsid)
|
|||
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:
|
||||
case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
|
||||
case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
|
||||
case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -988,6 +973,45 @@ Out:
|
|||
return epm;
|
||||
}
|
||||
|
||||
static char*
|
||||
verifyDHparams(TlsConnection *c, Bytes *par, Bytes *sig, int sigalg)
|
||||
{
|
||||
uchar hashes[MD5dlen+SHA1dlen], *buf;
|
||||
Bytes *blob;
|
||||
RSApub *pk;
|
||||
char *err;
|
||||
|
||||
pk = X509toRSApub(c->cert->data, c->cert->len, nil, 0);
|
||||
if(pk == nil)
|
||||
return "bad certificate";
|
||||
|
||||
blob = newbytes(2*RandomSize + par->len);
|
||||
memmove(blob->data+0*RandomSize, c->crandom, RandomSize);
|
||||
memmove(blob->data+1*RandomSize, c->srandom, RandomSize);
|
||||
memmove(blob->data+2*RandomSize, par->data, par->len);
|
||||
if(c->version >= TLS12Version) {
|
||||
if((sigalg & 0xFF) == 1)
|
||||
err = X509verifydata(sig->data, sig->len, blob->data, blob->len, pk);
|
||||
else
|
||||
err = "signaure algorithm not RSA";
|
||||
} else {
|
||||
err = nil;
|
||||
if(pkcs1decryptsignature(sig->data, sig->len, pk, &buf) != sizeof(hashes))
|
||||
err = "bad signature";
|
||||
else {
|
||||
md5(blob->data, blob->len, hashes, nil);
|
||||
sha1(blob->data, blob->len, hashes+MD5dlen, nil);
|
||||
if(memcmp(buf, hashes, sizeof(hashes)) != 0)
|
||||
err = "digests did not match";
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
freebytes(blob);
|
||||
rsapubfree(pk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static TlsConnection *
|
||||
tlsClient2(int ctl, int hand, uchar *csid, int ncsid, uchar *cert, int certlen, uchar *ext, int extlen,
|
||||
int (*trace)(char*fmt, ...))
|
||||
|
@ -1077,10 +1101,20 @@ tlsClient2(int ctl, int hand, uchar *csid, int ncsid, uchar *cert, int certlen,
|
|||
if(!msgRecv(c, &m))
|
||||
goto Err;
|
||||
if(m.tag == HServerKeyExchange) {
|
||||
char *err;
|
||||
|
||||
if(!dhx){
|
||||
tlsError(c, EUnexpectedMessage, "got an server key exchange");
|
||||
goto Err;
|
||||
}
|
||||
err = verifyDHparams(c,
|
||||
m.u.serverKeyExchange.dh_parameters,
|
||||
m.u.serverKeyExchange.dh_signature,
|
||||
m.u.serverKeyExchange.sigalg);
|
||||
if(err != nil){
|
||||
tlsError(c, EBadCertificate, "can't verify dh parameters: %s", err);
|
||||
goto Err;
|
||||
}
|
||||
if(isECDHE(cipher))
|
||||
epm = tlsSecECDHEc(c->sec, c->srandom, c->version,
|
||||
m.u.serverKeyExchange.curve,
|
||||
|
@ -1447,7 +1481,7 @@ tlsReadN(TlsConnection *c, int n)
|
|||
static int
|
||||
msgRecv(TlsConnection *c, Msg *m)
|
||||
{
|
||||
uchar *p;
|
||||
uchar *p, *s;
|
||||
int type, n, nn, i, nsid, nrandom, nciph;
|
||||
|
||||
for(;;) {
|
||||
|
@ -1691,6 +1725,7 @@ msgRecv(TlsConnection *c, Msg *m)
|
|||
case HServerKeyExchange:
|
||||
if(n < 2)
|
||||
goto Short;
|
||||
s = p;
|
||||
if(isECDHE(c->cipher)){
|
||||
nn = *p;
|
||||
p++, n--;
|
||||
|
@ -1734,6 +1769,7 @@ msgRecv(TlsConnection *c, Msg *m)
|
|||
/* should not happen */
|
||||
break;
|
||||
}
|
||||
m->u.serverKeyExchange.dh_parameters = makebytes(s, p - s);
|
||||
if(n >= 2){
|
||||
m->u.serverKeyExchange.sigalg = 0;
|
||||
if(c->version >= TLS12Version){
|
||||
|
@ -1835,6 +1871,7 @@ msgClear(Msg *m)
|
|||
freebytes(m->u.serverKeyExchange.dh_p);
|
||||
freebytes(m->u.serverKeyExchange.dh_g);
|
||||
freebytes(m->u.serverKeyExchange.dh_Ys);
|
||||
freebytes(m->u.serverKeyExchange.dh_parameters);
|
||||
freebytes(m->u.serverKeyExchange.dh_signature);
|
||||
break;
|
||||
case HClientKeyExchange:
|
||||
|
@ -1951,6 +1988,7 @@ msgPrint(char *buf, int n, Msg *m)
|
|||
bs = bytesPrint(bs, be, "\tdh_Ys: ", m->u.serverKeyExchange.dh_Ys, "\n");
|
||||
if(m->u.serverKeyExchange.sigalg != 0)
|
||||
bs = seprint(bs, be, "\tsigalg: %.4x\n", m->u.serverKeyExchange.sigalg);
|
||||
bs = bytesPrint(bs, be, "\tdh_parameters: ", m->u.serverKeyExchange.dh_parameters, "\n");
|
||||
bs = bytesPrint(bs, be, "\tdh_signature: ", m->u.serverKeyExchange.dh_signature, "\n");
|
||||
break;
|
||||
case HClientKeyExchange:
|
||||
|
|
|
@ -2167,60 +2167,108 @@ digest_certinfo(Bytes *cert, DigestAlg *da, uchar *digest)
|
|||
return da->len;
|
||||
}
|
||||
|
||||
static char*
|
||||
verify_signature(Bytes* signature, RSApub *pk, uchar *edigest, int edigestlen, Elem **psigalg)
|
||||
int
|
||||
pkcs1decryptsignature(uchar *sig, int siglen, RSApub *pk, uchar **pbuf)
|
||||
{
|
||||
Elem e;
|
||||
Elist *el;
|
||||
Bytes *digest;
|
||||
uchar *pkcs1buf, *buf;
|
||||
int buflen;
|
||||
int nlen, buflen;
|
||||
mpint *pkcs1;
|
||||
int nlen;
|
||||
char *err;
|
||||
uchar *buf;
|
||||
|
||||
err = nil;
|
||||
pkcs1buf = nil;
|
||||
*pbuf = nil;
|
||||
|
||||
/* one less than the byte length of the modulus */
|
||||
nlen = (mpsignif(pk->n)-1)/8;
|
||||
|
||||
/* see 9.2.1 of rfc2437 */
|
||||
pkcs1 = betomp(signature->data, signature->len, nil);
|
||||
pkcs1 = betomp(sig, siglen, nil);
|
||||
mpexp(pkcs1, pk->ek, pk->n, pkcs1);
|
||||
buflen = mptobe(pkcs1, nil, 0, &pkcs1buf);
|
||||
buf = pkcs1buf;
|
||||
if(buflen != nlen || buf[0] != 1) {
|
||||
err = "expected 1";
|
||||
goto end;
|
||||
}
|
||||
buflen = mptobe(pkcs1, nil, 0, pbuf);
|
||||
mpfree(pkcs1);
|
||||
|
||||
buf = *pbuf;
|
||||
if(buflen != nlen || buf[0] != 1)
|
||||
goto bad;
|
||||
buf++, buflen--;
|
||||
while(buflen > 0 && buf[0] == 0xff)
|
||||
buf++, buflen--;
|
||||
if(buflen < 1 || buf[0] != 0) {
|
||||
err = "expected 0";
|
||||
goto end;
|
||||
}
|
||||
if(buflen < 1 || buf[0] != 0)
|
||||
goto bad;
|
||||
buf++, buflen--;
|
||||
if(decode(buf, buflen, &e) != ASN_OK || !is_seq(&e, &el) || elistlen(el) != 2 ||
|
||||
memmove(*pbuf, buf, buflen);
|
||||
return buflen;
|
||||
bad:
|
||||
free(*pbuf);
|
||||
*pbuf = nil;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char*
|
||||
verify_digestinfo(uchar *sig, int siglen, RSApub *pk, uchar *pdigest, int *psigalg)
|
||||
{
|
||||
Elem e;
|
||||
Elist *el;
|
||||
Bytes *digest;
|
||||
uchar *buf;
|
||||
int buflen;
|
||||
char *err;
|
||||
|
||||
el = nil;
|
||||
buflen = pkcs1decryptsignature(sig, siglen, pk, &buf);
|
||||
if(buflen < 0 || decode(buf, buflen, &e) != ASN_OK || !is_seq(&e, &el) || elistlen(el) != 2 ||
|
||||
!is_octetstring(&el->tl->hd, &digest)) {
|
||||
err = "signature parse error";
|
||||
goto end;
|
||||
}
|
||||
*psigalg = &el->hd;
|
||||
if(digest->len != edigestlen) {
|
||||
*psigalg = parse_alg(&el->hd);
|
||||
if(*psigalg < 0){
|
||||
err = "unknown signature algorithm";
|
||||
goto end;
|
||||
}
|
||||
if(digest->len != digestalg[*psigalg]->len){
|
||||
err = "bad digest length";
|
||||
goto end;
|
||||
}
|
||||
if(memcmp(digest->data, edigest, edigestlen) != 0)
|
||||
err = "digests did not match";
|
||||
|
||||
memmove(pdigest, digest->data, digest->len);
|
||||
err = nil;
|
||||
end:
|
||||
mpfree(pkcs1);
|
||||
free(pkcs1buf);
|
||||
freevalfields(&e.val);
|
||||
free(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
char*
|
||||
X509verifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, RSApub *pk)
|
||||
{
|
||||
uchar digest[MAXdlen];
|
||||
int sigalg;
|
||||
char *e;
|
||||
|
||||
e = verify_digestinfo(sig, siglen, pk, digest, &sigalg);
|
||||
if(e != nil)
|
||||
return e;
|
||||
if(digestalg[sigalg]->len != edigestlen)
|
||||
return "bad digest length";
|
||||
if(memcmp(digest, edigest, edigestlen) != 0)
|
||||
return "digests did not match";
|
||||
return nil;
|
||||
}
|
||||
|
||||
char*
|
||||
X509verifydata(uchar *sig, int siglen, uchar *data, int datalen, RSApub *pk)
|
||||
{
|
||||
uchar digest[MAXdlen], edigest[MAXdlen];
|
||||
int sigalg;
|
||||
char *e;
|
||||
|
||||
e = verify_digestinfo(sig, siglen, pk, digest, &sigalg);
|
||||
if(e != nil)
|
||||
return e;
|
||||
(*digestalg[sigalg]->fun)(data, datalen, edigest, nil);
|
||||
if(memcmp(digest, edigest, digestalg[sigalg]->len) != 0)
|
||||
return "digests did not match";
|
||||
return nil;
|
||||
}
|
||||
|
||||
RSApub*
|
||||
X509toRSApub(uchar *cert, int ncert, char *name, int nname)
|
||||
{
|
||||
|
@ -2253,7 +2301,6 @@ X509verify(uchar *cert, int ncert, RSApub *pk)
|
|||
CertX509 *c;
|
||||
int digestlen;
|
||||
uchar digest[MAXdlen];
|
||||
Elem *sigalg;
|
||||
|
||||
b = makebytes(cert, ncert);
|
||||
c = decode_cert(b);
|
||||
|
@ -2267,7 +2314,7 @@ X509verify(uchar *cert, int ncert, RSApub *pk)
|
|||
freecert(c);
|
||||
return "cannot decode certinfo";
|
||||
}
|
||||
e = verify_signature(c->signature, pk, digest, digestlen, &sigalg);
|
||||
e = X509verifydigest(c->signature->data, c->signature->len, digest, digestlen, pk);
|
||||
freecert(c);
|
||||
return e;
|
||||
}
|
||||
|
@ -2674,7 +2721,6 @@ X509dump(uchar *cert, int ncert)
|
|||
RSApub *pk;
|
||||
int digestlen;
|
||||
uchar digest[MAXdlen];
|
||||
Elem *sigalg;
|
||||
|
||||
print("begin X509dump\n");
|
||||
b = makebytes(cert, ncert);
|
||||
|
@ -2700,14 +2746,10 @@ X509dump(uchar *cert, int ncert)
|
|||
print("pubkey e=%B n(%d)=%B\n", pk->ek, mpsignif(pk->n), pk->n);
|
||||
|
||||
print("sigalg=%d digest=%.*H\n", c->signature_alg, digestlen, digest);
|
||||
e = verify_signature(c->signature, pk, digest, digestlen, &sigalg);
|
||||
if(e==nil){
|
||||
e = X509verifydigest(c->signature->data, c->signature->len, digest, digestlen, pk);
|
||||
if(e==nil)
|
||||
e = "nil (meaning ok)";
|
||||
print("sigalg=\n");
|
||||
if(sigalg)
|
||||
edump(*sigalg);
|
||||
}
|
||||
print("self-signed verify_signature returns: %s\n", e);
|
||||
print("self-signed X509verifydigest returns: %s\n", e);
|
||||
|
||||
rsapubfree(pk);
|
||||
freecert(c);
|
||||
|
|
Loading…
Reference in a new issue