--- sys/src/libsec/port/tlshand.c
+++ sys/src/libsec/port/tlshand.c
@@ -88,6 +88,7 @@ typedef struct TlsConnection{
// for finished messages
HandHash hs; // handshake hash
Finished finished;
+ int serverSignFlags; /* AlgRsaSign|AlgEcSign; gates okCipher */
int serverCurve; /* negotiated ECDHE group, 0 = none */
} TlsConnection;
@@ -597,6 +598,32 @@ tlsServer2(int ctl, int hand, uchar *cert, int ncert,
memmove(c->crandom, m.u.clientHello.random, RandomSize);
/*
+ * Derive c->serverSignFlags from the server's cert SPKI key type
+ * (RFC 4492 Section 2.2): RSA cert -> AlgRsaSign, ECDSA cert ->
+ * AlgEcSign. okCipher() consults this to skip cipher suites
+ * whose authentication algorithm doesn't match the loaded cert;
+ * without the gate, an RSA-cert server can pick TLS_ECDHE_ECDSA_*
+ * and emit a (cipher, sigalg) pair that strict TLS 1.2 clients
+ * will reject.
+ */
+ {
+ RSApub *rpk;
+ ECpub *epk;
+ ECdomain dom;
+
+ rpk = X509toRSApub(cert, ncert, nil, 0);
+ if(rpk != nil){
+ c->serverSignFlags |= AlgRsaSign;
+ rsapubfree(rpk);
+ }
+ epk = X509toECpub(cert, ncert, nil, 0, &dom);
+ if(epk != nil){
+ c->serverSignFlags |= AlgEcSign;
+ ecpubfree(epk);
+ ecdomfree(&dom);
+ }
+ }
+ /*
* Walk client's supported_groups in preference order and pick the first
* curve we also implement (RFC 8422 Section 5.1.1, RFC 7919 Section 4).
* Inline parse rather than going through the extension framework so this
@@ -1965,6 +1992,7 @@ okCipher(TlsConnection *c, Ints *cv)
{
int weak, i, j, id;
+ /* track if client offered only weak ciphers */
weak = 1;
for(i = 0; i < cv->len; i++) {
id = cv->data[i];
@@ -1972,9 +2000,29 @@ okCipher(TlsConnection *c, Ints *cv)
weak = 0;
else
weak &= weakCipher[id];
- for(j = 0; j < nelem(cipherAlgs); j++)
- if(cipherAlgs[j].ok && cipherAlgs[j].tlsid == id)
- return id;
+ }
+ /* server preference: iterate cipherAlgs[] in our preferred
+ * order, pick first that the client also offered. AEAD
+ * entries are listed first in cipherAlgs[] so AEAD wins
+ * over CBC when both are offered. */
+ for(j = 0; j < nelem(cipherAlgs); j++) {
+ if(!cipherAlgs[j].ok)
+ continue;
+ /* RFC 8422 Section 5.1.1 + RFC 7919 Section 4: skip ECDHE
+ * suites when the client and we share no named curve. */
+ if(c != nil && isECDHE(cipherAlgs[j].tlsid) && c->serverCurve == 0)
+ continue;
+ /* RFC 4492 Section 2.2: cipher's authentication algorithm
+ * must match the server cert's key type. Skip
+ * TLS_ECDHE_ECDSA_* against an RSA cert and TLS_ECDHE_RSA_*
+ * / TLS_RSA_* against an ECDSA-only cert.
+ */
+ if(c != nil && cipherAlgs[j].flags != 0
+ && (cipherAlgs[j].flags & c->serverSignFlags) == 0)
+ continue;
+ for(i = 0; i < cv->len; i++)
+ if(cv->data[i] == cipherAlgs[j].tlsid)
+ return cipherAlgs[j].tlsid;
}
if(weak)
return -2;
|