Summary:
A TLS client appears to be failing to negotiate when the server hands over a 127-byte pubkey in the DHE_RSA Server Key Exchange message, but succeeding when it hands over a 128-byte pubkey. What's the deal with pubkey length, and specifically, is this legitimate behavior on the server's part?
Gory details:
I have a client (software unknown) that's experiencing intermittent failures connecting to my TLS server (F5, TLS 1.2). Successful and failed connections all settle upon Cipher Suite TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x009f) in the Server Hello. All connection attempts reach Server Hello Done and failed connections are FINned by the client immediately following that.
Looking at packet captures, the failures all seem to coincide with a "Pubkey Length" of 127 in the Server Key Exchange handshake:
On the other hand, every successful negotiation with this client has involved a "Pubkey Length" of 128 in the Server Key Exchange handshake:
Whenever the server sends the 127-byte version, the client responds with a FIN/ACK instead of the Client Key Exchange. The conclusion we've come to is that the 127-byte pubkey length is not acceptable to the client. I've read RFC 5246 to see if I can get an idea for what's allowed and they're not helpful (to me, at least!):
struct {
opaque dh_p<1..2^16-1>;
opaque dh_g<1..2^16-1>;
opaque dh_Ys<1..2^16-1>;
} ServerDHParams; /* Ephemeral DH parameters */
struct {
select (KeyExchangeAlgorithm) {
case dhe_rsa:
ServerDHParams params;
digitally-signed struct {
opaque client_random[32];
opaque server_random[32];
ServerDHParams params;
} signed_params;
};
} ServerKeyExchange;
So the specific questions are:
What's the deal with Pubkey Length in DHE_RSA Server Key Exchange? Specifically, does 127 vs. 128 represent something like zero-truncation to save length, or is it truly providing a 127-byte key?
More specifically, is 127 a valid Pubkey Length that the client should be able to handle, or could this represent an issue with the server that needs addressing?
Clarification:
The server in question is an F5 load balancer running an up-to-date (11.6) software, so it's not only off-the-shelf but off-a-reasonably-high-shelf when it comes to TLS provenance. That's not to say it can't be the issue, just to say that it's not like I'm rolling my own Lua-over-Django-with-third-Rails TLS server in the backend :)
What OpenSSL thinks
I left a script using openssl s_client
to hit the server every minute over the weekend, and captured full packets, so that I could see what OpenSSL thinks of this particular server configuration. And simply put, with a 127-byte DHE "Pubkey" length, OpenSSL considers the "Server Temp Key" 1024-bit.
Here's the Server Key Exchange as displayed in Wireshark:
And here's the OpenSSL output for that connection:
$ tail -25 20151107234244.txt
---
No client certificate CA names sent
Server Temp Key: DH, 1024 bits
---
SSL handshake has read 2014 bytes and written 291 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1.2
Cipher : DHE-RSA-AES256-GCM-SHA384
Session-ID: 8C48CAD47FC01AF350CF618E21AE9C7E8764BB7C7243D8D6204F95634523EF2F
Session-ID-ctx:
Master-Key: FB80D1070A3BB2435C2D4E50D6633DA3DE4FDCFA5C8A922E17EC6FB0EDC41E259F55DFC33345B51F9A90568B36CFBB7C
Key-Arg : None
Krb5 Principal: None
PSK identity: None
PSK identity hint: None
Start Time: 1446957764
Timeout : 300 (sec)
Verify return code: 21 (unable to verify the first certificate)
---
$
So is it 1024 or 1016 bit? Does the 'p' length indicate key strength instead of the Pubkey Length (ssl.handshake.ys_len for those following along in Wireshark)? Where is "Server Key Exchange" for Diffie-Hellman defined? Someone found enough documentation to write a Wireshark parser, if I could look at the doco they used maybe I'd better understand the expectations around these fields.
ServerKeyExchange
is defined in rfc5246 at the link you already posted (and its predecessors). DHE (different from static or anon DH at the protocol level) iscase dhe_dss: case dhe_rsa: ServerDHParams params; digitally-signed [irrelevant]
andServerDHParams
isopaque dh_p<1..2^16-1>; opaque dh_g<1..2^16-1>; opaque dh_Ys<1..2^16-1>;
. The variable length vectors (4.3 in the same document) implies the encodings don't require padding zeros, although it doesn't say so explicitly AFAICS.DHE_RSA
key exchange. Every once in the while the response from a server is one byte shorter than normal (traced to theServerDHParams
struct in theServerKeyExchange
message), andInitializeSecurityContext()
returns the errorSEC_E_BUFFER_TOO_SMALL
.