Skip to content

Commit f6dd826

Browse files
Fix MFLN probe and allow returning whether MFLN succeeded or not after a connection. (#6000)
Fixes #5996 * Add extensions to probe message for EC, others probeMFLN was failing on some connection attempts to servers which only supported EC based ciphers because it did not include the proper TLS handshake extensions to list what kinds of ECs it supported. Add those to the probeMFLN ClientHello message to make probes pass. * Add client.getMFLNStatus method, returns MFLN state After a connection it is useful to check whether MFLN negotiation succeeded. getMFLNStatus returns a bool (valid only after client.connect() succeeds, of course) indicating whether the requested buffer sizes were negotiated successfully.
1 parent d9b0480 commit f6dd826

File tree

10 files changed

+73
-10
lines changed

10 files changed

+73
-10
lines changed

doc/esp8266wifi/bearssl-client-secure-class.rst

+5
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,11 @@ Once you have verified (or know beforehand) that MFLN is supported you can use t
180180

181181
In certain applications where the TLS server does not support MFLN (not many do as of this writing as it is relatively new to OpenSSL), but you control both the ESP8266 and the server to which it is communicating, you may still be able to `setBufferSizes()` smaller if you guarantee no chunk of data will overflow those buffers.
182182

183+
bool getMFLNStatus()
184+
^^^^^^^^^^^^^^^^^^^^
185+
186+
After a successful connection, this method returns whether or not MFLN negotiation succeeded or not. If it did not succeed, and you reduced the receive buffer with `setBufferSizes` then you may experience reception errors if the server attempts to send messages larger than your receive buffer.
187+
183188
Sessions (Resuming connections fast)
184189
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
185190

libraries/ESP8266WiFi/examples/BearSSL_MaxFragmentLength/BearSSL_MaxFragmentLength.ino

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ int fetchMaxFragmentLength() {
8787
}
8888
client.connect("tls.mbed.org", 443);
8989
if (client.connected()) {
90+
Serial.printf("MFLN status: %s\n", client.getMFLNStatus() ? "true" : "false");
9091
Serial.printf("Memory used: %d\n", ret - ESP.getFreeHeap());
9192
ret -= ESP.getFreeHeap();
9293
fetch(&client);

libraries/ESP8266WiFi/keywords.txt

+1
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ setBufferSizes KEYWORD2
183183
getLastSSLError KEYWORD2
184184
setCertStore KEYWORD2
185185
probeMaxFragmentLength KEYWORD2
186+
getMFLNStatus KEYWORD2
186187

187188
#WiFiServerBearSSL
188189
setRSACert KEYWORD2

libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp

+44-5
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,7 @@ bool WiFiClientSecure::_connectSSL(const char* hostName) {
965965
return false;
966966
}
967967
br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size);
968+
968969
// Apply any client certificates, if supplied.
969970
if (_sk && _sk->isRSA()) {
970971
br_ssl_client_set_single_rsa(_sc.get(), _chain ? _chain->getX509Certs() : nullptr, _chain ? _chain->getCount() : 0,
@@ -1257,7 +1258,13 @@ bool WiFiClientSecure::probeMaxFragmentLength(IPAddress ip, uint16_t port, uint1
12571258
// 0xc0, 0x13, // BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
12581259
static const uint8_t clientHelloTail_P[] PROGMEM = {
12591260
0x01, 0x00, // No compression
1260-
0x00, 0x05, // Extension length
1261+
0x00, 26 + 14 + 6 + 5, // Extension length
1262+
0x00, 0x0d, 0x00, 0x16, 0x00, 0x14, 0x04, 0x03, 0x03, 0x03, 0x05, 0x03,
1263+
0x06, 0x03, 0x02, 0x03, 0x04, 0x01, 0x03, 0x01, 0x05, 0x01, 0x06,
1264+
0x01, 0x02, 0x01, // Supported signature algorithms
1265+
0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19,
1266+
0x00, 0x1d, // Supported groups
1267+
0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, // Supported EC formats
12611268
0x00, 0x01, // Max Frag Len
12621269
0x00, 0x01, // len of MaxFragLen
12631270
};
@@ -1322,6 +1329,8 @@ bool WiFiClientSecure::probeMaxFragmentLength(IPAddress ip, uint16_t port, uint1
13221329
uint8_t sessionLen;
13231330
uint8_t cipher[2];
13241331
uint8_t comp;
1332+
uint8_t extBytes[2];
1333+
uint16_t extLen;
13251334

13261335
ret = probe.readBytes(fragResp, 5);
13271336
if (!probe.connected() || (ret != 5) || (fragResp[0] != 0x16) || (fragResp[1] != 0x03) || (fragResp[2] != 0x03)) {
@@ -1388,10 +1397,40 @@ bool WiFiClientSecure::probeMaxFragmentLength(IPAddress ip, uint16_t port, uint1
13881397
// short read or invalid compression
13891398
return _SendAbort(probe, supportsLen);
13901399
}
1391-
if (handLen > 0) {
1392-
// At this point, having an extension present means that the extension we
1393-
// sent was accepted.
1394-
supportsLen = true;
1400+
1401+
ret = probe.readBytes(extBytes, 2);
1402+
handLen -= ret;
1403+
extLen = extBytes[1] || (extBytes[0]<<8);
1404+
if ((extLen == 0) || (ret != 2)) {
1405+
return _SendAbort(probe, supportsLen);
1406+
}
1407+
1408+
while (handLen > 0) {
1409+
// Parse each extension and look for MFLN
1410+
uint8_t typeBytes[2];
1411+
ret = probe.readBytes(typeBytes, 2);
1412+
handLen -= 2;
1413+
if ((ret != 2) || (handLen <= 0) ) {
1414+
return _SendAbort(probe, supportsLen);
1415+
}
1416+
uint8_t lenBytes[2];
1417+
ret = probe.readBytes(lenBytes, 2);
1418+
handLen -= 2;
1419+
uint16_t extLen = lenBytes[1] | (lenBytes[0]<<8);
1420+
if ((ret != 2) || (handLen <= 0) || (extLen > 32) || (extLen > handLen) ) {
1421+
return _SendAbort(probe, supportsLen);
1422+
}
1423+
if ((typeBytes[0]==0x00) && (typeBytes[1]==0x01)) { // MFLN extension!
1424+
// If present and 1-byte in length, it's supported
1425+
return _SendAbort(probe, extLen==1 ? true : false);
1426+
}
1427+
// Skip the extension, move to next one
1428+
uint8_t junk[32];
1429+
ret = probe.readBytes(junk, extLen);
1430+
handLen -= extLen;
1431+
if (ret != extLen) {
1432+
return _SendAbort(probe, supportsLen);
1433+
}
13951434
}
13961435
return _SendAbort(probe, supportsLen);
13971436
}

libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ class WiFiClientSecure : public WiFiClient {
103103
// Sets the requested buffer size for transmit and receive
104104
void setBufferSizes(int recv, int xmit);
105105

106+
// Returns whether MFLN negotiation for the above buffer sizes succeeded (after connection)
107+
int getMFLNStatus() {
108+
return connected() && br_ssl_engine_get_mfln_negotiated(_eng);
109+
}
110+
106111
// Return an error code and possibly a text string in a passed-in buffer with last SSL failure
107112
int getLastSSLError(char *dest = NULL, size_t len = 0);
108113

@@ -117,7 +122,7 @@ class WiFiClientSecure : public WiFiClient {
117122
bool setCiphers(std::vector<uint16_t> list);
118123
bool setCiphersLessSecure(); // Only use the limited set of RSA ciphers without EC
119124

120-
// Check for Maximum Fragment Length support for given len
125+
// Check for Maximum Fragment Length support for given len before connection (possibly insecure)
121126
static bool probeMaxFragmentLength(IPAddress ip, uint16_t port, uint16_t len);
122127
static bool probeMaxFragmentLength(const char *hostname, uint16_t port, uint16_t len);
123128
static bool probeMaxFragmentLength(const String& host, uint16_t port, uint16_t len);
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
// Do not edit -- Automatically generated by tools/sdk/ssl/bearssl/Makefile
2-
#define BEARSSL_GIT 6778687
2+
#define BEARSSL_GIT a143020

tools/sdk/include/bearssl/bearssl_hash.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,7 @@ void br_sha256_update(br_sha256_context *ctx, const void *data, size_t len);
724724
*/
725725
void br_sha256_out(const br_sha256_context *ctx, void *out);
726726

727-
#if BR_DOXYGEN_IGNORE
727+
#ifdef BR_DOXYGEN_IGNORE
728728
/**
729729
* \brief Save SHA-256 running state.
730730
*
@@ -742,7 +742,7 @@ uint64_t br_sha256_state(const br_sha256_context *ctx, void *out);
742742
#define br_sha256_state br_sha224_state
743743
#endif
744744

745-
#if BR_DOXYGEN_IGNORE
745+
#ifdef BR_DOXYGEN_IGNORE
746746
/**
747747
* \brief Restore SHA-256 running state.
748748
*

tools/sdk/include/bearssl/bearssl_ssl.h

+12
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,7 @@ typedef struct {
864864
*/
865865
uint16_t max_frag_len;
866866
unsigned char log_max_frag_len;
867+
unsigned char max_frag_len_negotiated;
867868
unsigned char peer_log_max_frag_len;
868869

869870
/*
@@ -1830,6 +1831,17 @@ void br_ssl_engine_set_buffer(br_ssl_engine_context *cc,
18301831
void br_ssl_engine_set_buffers_bidi(br_ssl_engine_context *cc,
18311832
void *ibuf, size_t ibuf_len, void *obuf, size_t obuf_len);
18321833

1834+
/**
1835+
* \brief Determine if MFLN negotiation was successful
1836+
*
1837+
* \param cc SSL engine context.
1838+
*/
1839+
static inline uint8_t
1840+
br_ssl_engine_get_mfln_negotiated(br_ssl_engine_context *cc)
1841+
{
1842+
return cc->max_frag_len_negotiated;
1843+
}
1844+
18331845
/**
18341846
* \brief Inject some "initial entropy" in the context.
18351847
*

tools/sdk/lib/libbearssl.a

1.12 KB
Binary file not shown.

0 commit comments

Comments
 (0)