Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 00f9624

Browse filesBrowse files
chemicstryme-no-dev
authored andcommitted
Port SSL fingerprint checking from ESP8266 WiFiClientSecure to ESP32 (espressif#1397)
1 parent 0ea9ea4 commit 00f9624
Copy full SHA for 00f9624

File tree

Expand file treeCollapse file tree

4 files changed

+157
-0
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+157
-0
lines changed

‎libraries/WiFiClientSecure/src/WiFiClientSecure.cpp

Copy file name to clipboardExpand all lines: libraries/WiFiClientSecure/src/WiFiClientSecure.cpp
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,14 @@ void WiFiClientSecure::setPrivateKey (const char *private_key)
210210
_private_key = private_key;
211211
}
212212

213+
bool WiFiClientSecure::verify(const char* fp, const char* domain_name)
214+
{
215+
if (!sslclient)
216+
return false;
217+
218+
return verify_ssl_fingerprint(sslclient, fp, domain_name);
219+
}
220+
213221
int WiFiClientSecure::lastError(char *buf, const size_t size)
214222
{
215223
if (!_lastError) {

‎libraries/WiFiClientSecure/src/WiFiClientSecure.h

Copy file name to clipboardExpand all lines: libraries/WiFiClientSecure/src/WiFiClientSecure.h
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class WiFiClientSecure : public WiFiClient
5858
void setCACert(const char *rootCA);
5959
void setCertificate(const char *client_ca);
6060
void setPrivateKey (const char *private_key);
61+
bool verify(const char* fingerprint, const char* domain_name);
6162

6263
operator bool()
6364
{

‎libraries/WiFiClientSecure/src/ssl_client.cpp

Copy file name to clipboardExpand all lines: libraries/WiFiClientSecure/src/ssl_client.cpp
+146Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
#include <lwip/sockets.h>
1313
#include <lwip/sys.h>
1414
#include <lwip/netdb.h>
15+
#include <mbedtls/sha256.h>
16+
#include <mbedtls/oid.h>
17+
#include <algorithm>
18+
#include <string>
1519
#include "ssl_client.h"
1620

1721

@@ -262,3 +266,145 @@ int get_ssl_receive(sslclient_context *ssl_client, uint8_t *data, int length)
262266
//log_v( "%d bytes read", ret); //for low level debug
263267
return ret;
264268
}
269+
270+
static bool parseHexNibble(char pb, uint8_t* res)
271+
{
272+
if (pb >= '0' && pb <= '9') {
273+
*res = (uint8_t) (pb - '0'); return true;
274+
} else if (pb >= 'a' && pb <= 'f') {
275+
*res = (uint8_t) (pb - 'a' + 10); return true;
276+
} else if (pb >= 'A' && pb <= 'F') {
277+
*res = (uint8_t) (pb - 'A' + 10); return true;
278+
}
279+
return false;
280+
}
281+
282+
// Compare a name from certificate and domain name, return true if they match
283+
static bool matchName(const std::string& name, const std::string& domainName)
284+
{
285+
size_t wildcardPos = name.find('*');
286+
if (wildcardPos == std::string::npos) {
287+
// Not a wildcard, expect an exact match
288+
return name == domainName;
289+
}
290+
291+
size_t firstDotPos = name.find('.');
292+
if (wildcardPos > firstDotPos) {
293+
// Wildcard is not part of leftmost component of domain name
294+
// Do not attempt to match (rfc6125 6.4.3.1)
295+
return false;
296+
}
297+
if (wildcardPos != 0 || firstDotPos != 1) {
298+
// Matching of wildcards such as baz*.example.com and b*z.example.com
299+
// is optional. Maybe implement this in the future?
300+
return false;
301+
}
302+
size_t domainNameFirstDotPos = domainName.find('.');
303+
if (domainNameFirstDotPos == std::string::npos) {
304+
return false;
305+
}
306+
return domainName.substr(domainNameFirstDotPos) == name.substr(firstDotPos);
307+
}
308+
309+
// Verifies certificate provided by the peer to match specified SHA256 fingerprint
310+
bool verify_ssl_fingerprint(sslclient_context *ssl_client, const char* fp, const char* domain_name)
311+
{
312+
// Convert hex string to byte array
313+
uint8_t fingerprint_local[32];
314+
int len = strlen(fp);
315+
int pos = 0;
316+
for (size_t i = 0; i < sizeof(fingerprint_local); ++i) {
317+
while (pos < len && ((fp[pos] == ' ') || (fp[pos] == ':'))) {
318+
++pos;
319+
}
320+
if (pos > len - 2) {
321+
log_d("pos:%d len:%d fingerprint too short", pos, len);
322+
return false;
323+
}
324+
uint8_t high, low;
325+
if (!parseHexNibble(fp[pos], &high) || !parseHexNibble(fp[pos+1], &low)) {
326+
log_d("pos:%d len:%d invalid hex sequence: %c%c", pos, len, fp[pos], fp[pos+1]);
327+
return false;
328+
}
329+
pos += 2;
330+
fingerprint_local[i] = low | (high << 4);
331+
}
332+
333+
// Get certificate provided by the peer
334+
const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx);
335+
336+
if (!crt)
337+
{
338+
log_d("could not fetch peer certificate");
339+
return false;
340+
}
341+
342+
// Calculate certificate's SHA256 fingerprint
343+
uint8_t fingerprint_remote[32];
344+
mbedtls_sha256_context sha256_ctx;
345+
mbedtls_sha256_init(&sha256_ctx);
346+
mbedtls_sha256_starts(&sha256_ctx, false);
347+
mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len);
348+
mbedtls_sha256_finish(&sha256_ctx, fingerprint_remote);
349+
350+
// Check if fingerprints match
351+
if (memcmp(fingerprint_local, fingerprint_remote, 32))
352+
{
353+
log_d("fingerprint doesn't match");
354+
return false;
355+
}
356+
357+
// Additionally check if certificate has domain name if provided
358+
if (domain_name)
359+
return verify_ssl_dn(ssl_client, domain_name);
360+
else
361+
return true;
362+
}
363+
364+
// Checks if peer certificate has specified domain in CN or SANs
365+
bool verify_ssl_dn(sslclient_context *ssl_client, const char* domain_name)
366+
{
367+
log_d("domain name: '%s'", (domain_name)?domain_name:"(null)");
368+
std::string domain_name_str(domain_name);
369+
std::transform(domain_name_str.begin(), domain_name_str.end(), domain_name_str.begin(), ::tolower);
370+
371+
// Get certificate provided by the peer
372+
const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx);
373+
374+
// Check for domain name in SANs
375+
const mbedtls_x509_sequence* san = &crt->subject_alt_names;
376+
while (san != nullptr)
377+
{
378+
std::string san_str((const char*)san->buf.p, san->buf.len);
379+
std::transform(san_str.begin(), san_str.end(), san_str.begin(), ::tolower);
380+
381+
if (matchName(san_str, domain_name_str))
382+
return true;
383+
384+
log_d("SAN '%s': no match", san_str.c_str());
385+
386+
// Fetch next SAN
387+
san = san->next;
388+
}
389+
390+
// Check for domain name in CN
391+
const mbedtls_asn1_named_data* common_name = &crt->subject;
392+
while (common_name != nullptr)
393+
{
394+
// While iterating through DN objects, check for CN object
395+
if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &common_name->oid))
396+
{
397+
std::string common_name_str((const char*)common_name->val.p, common_name->val.len);
398+
399+
if (matchName(common_name_str, domain_name_str))
400+
return true;
401+
402+
log_d("CN '%s': not match", common_name_str.c_str());
403+
}
404+
405+
// Fetch next DN object
406+
common_name = common_name->next;
407+
}
408+
409+
return false;
410+
}

‎libraries/WiFiClientSecure/src/ssl_client.h

Copy file name to clipboardExpand all lines: libraries/WiFiClientSecure/src/ssl_client.h
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,7 @@ void stop_ssl_socket(sslclient_context *ssl_client, const char *rootCABuff, cons
3232
int data_to_read(sslclient_context *ssl_client);
3333
int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, uint16_t len);
3434
int get_ssl_receive(sslclient_context *ssl_client, uint8_t *data, int length);
35+
bool verify_ssl_fingerprint(sslclient_context *ssl_client, const char* fp, const char* domain_name);
36+
bool verify_ssl_dn(sslclient_context *ssl_client, const char* domain_name);
3537

3638
#endif

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.