--- sys/src/libsec/port/tlshand.c
+++ sys/src/libsec/port/tlshand.c
@@ -98,6 +98,7 @@ typedef struct TlsConnection{
* detection on ServerHello (RFC 5246 Section 7.4.1.4). */
int nClientOffered;
int clientOffered[16];
+ int extRenegInfoSeen; /* client signaled RFC 5746 (ext or SCSV) */
} TlsConnection;
typedef struct Msg{
@@ -710,6 +711,14 @@ tlsServer2(int ctl, int hand, uchar *cert, int ncert,
tlsError(c, EHandshakeFailure, "no matching compressor");
goto Err;
}
+ /*
+ * RFC 5746 Section 3.4: TLS_EMPTY_RENEGOTIATION_INFO_SCSV in the
+ * cipher list is equivalent to an empty renegotiation_info
+ * extension; either signal obliges the server to echo.
+ */
+ for(i = 0; i < m.u.clientHello.ciphers->len; i++)
+ if(m.u.clientHello.ciphers->data[i] == TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+ c->extRenegInfoSeen = 1;
/* Walk client's offered extensions through the parser table.
* Unknown types are silently skipped per RFC 5246 Section 7.4.1.4. */
@@ -2757,6 +2766,41 @@ buildEcPointFormatsAck(TlsConnection *c, ExtCtx *ctx,
return 2;
}
+/*
+ * RFC 5746 Section 3.6 server side: client may signal initial-handshake
+ * renegotiation_info via the empty extension (length 1, payload 0x00) or
+ * via TLS_EMPTY_RENEGOTIATION_INFO_SCSV in the cipher list. Either path
+ * obliges the server to echo an empty renegotiation_info in ServerHello.
+ * Plan 9 has no renegotiation primitive (devtls drops the handshake fd
+ * after `opened`); only initial-handshake echo matters.
+ */
+static int
+parseRenegInfoServer(TlsConnection *c, ExtCtx *ctx, uchar *in, int inlen)
+{
+ USED(ctx);
+ if(inlen != 1 || in[0] != 0)
+ return -1;
+ c->extRenegInfoSeen = 1;
+ return 0;
+}
+
+/*
+ * RFC 5746 Section 3.6: emit empty renegotiation_info ack iff client
+ * signaled (extension or SCSV); declined otherwise. Payload is a single
+ * length-prefix byte (0x00) for an empty renegotiated_connection.
+ */
+static int
+buildRenegInfoServer(TlsConnection *c, ExtCtx *ctx, uchar *out, int outsize)
+{
+ USED(ctx);
+ if(!c->extRenegInfoSeen)
+ return -1;
+ if(outsize < 1)
+ return -1;
+ out[0] = 0;
+ return 1;
+}
+
static ExtAlg clientHelloBuilders[] = {
{ ExtSni, buildSni, nil },
{ ExtSupportedGroups, buildSupportedGroups, nil },
@@ -2767,10 +2811,12 @@ static ExtAlg clientHelloParsers[] = {
{ ExtSni, nil, parseSniServer },
{ ExtEcPointFormats, nil, parseEcPointFormatsServer },
{ ExtSigalgs, nil, parseSigalgsServer },
+ { ExtRenegotiationInfo, nil, parseRenegInfoServer },
};
static ExtAlg serverHelloBuilders[] = {
{ ExtSni, buildSniAck, nil },
{ ExtEcPointFormats, buildEcPointFormatsAck, nil },
+ { ExtRenegotiationInfo, buildRenegInfoServer, nil },
};
/*
* serverHelloParsers is empty in this baseline -- the client side has
@@ -2862,6 +2908,11 @@ clientHelloBuildExt(TlsConnection *c, ExtCtx *ctx, uch
static int
clientHelloBuildExt(TlsConnection *c, ExtCtx *ctx, uchar *out, int outsize)
{
+ /* RFC 5746 Section 3.6: SCSV in the cipher list (makeciphers) is the
+ * RFC-equivalent signal of an empty renegotiation_info; the server
+ * MUST echo, so record the type up front to keep serverHelloParseExt's
+ * unsupported_extension(110) check from firing on the echo. */
+ c->clientOffered[c->nClientOffered++] = ExtRenegotiationInfo;
return buildExtensionBlock(c, ctx, clientHelloBuilders,
nelem(clientHelloBuilders), out, outsize);
}
|