Skip to content

Commit 7d3d227

Browse files
authored
Integrate certifi for SSL context handling
Added certifi support to SSL context creation and updated related documentation.
1 parent 28922a0 commit 7d3d227

File tree

1 file changed

+34
-35
lines changed

1 file changed

+34
-35
lines changed

tools/idf_tools.py

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
# the PATH to point to the installed tools and set up other environment variables
2929
# needed by the tools.
3030
import argparse
31+
import certifi
3132
import contextlib
3233
import copy
3334
import datetime
@@ -464,11 +465,15 @@ def parse_platform_arg(platform_str: str) -> str:
464465

465466
DL_CERT_DICT = {'dl.espressif.com': DIGICERT_ROOT_G2_CERT, 'github.com': DIGICERT_ROOT_CA_CERT}
466467

468+
def get_certifi_path() -> str:
469+
cert_path = certifi.where()
470+
if os.path.exists(cert_path):
471+
return cert_path
467472

468473
def create_esp_idf_ssl_context(url: str) -> ssl.SSLContext:
469474
"""
470-
Creates ESP-IDF optimized SSL context with OpenSSL version detection.
471-
475+
Creates ESP-IDF optimized SSL context with OpenSSL version detection and certifi support.
476+
472477
This function detects the SSL backend (LibreSSL vs OpenSSL) and creates
473478
an appropriate SSL context with version-specific optimizations. It also
474479
handles custom DigiCert certificates for known domains.
@@ -481,98 +486,92 @@ def create_esp_idf_ssl_context(url: str) -> ssl.SSLContext:
481486
"""
482487
ssl_version = ssl.OPENSSL_VERSION
483488
ssl_version_info = ssl.OPENSSL_VERSION_INFO
484-
489+
485490
info(f"SSL Backend: {ssl_version} ({ssl_version_info})")
486-
487-
# Create context based on detected SSL backend version
491+
492+
cafile = get_certifi_path()
493+
ctx = ssl.create_default_context(cafile=cafile) if cafile else ssl.create_default_context()
494+
488495
if "LibreSSL" in ssl_version:
489-
# macOS LibreSSL - more conservative settings
490-
ctx = ssl.create_default_context()
491496
ctx.set_ciphers('ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS')
492497
ctx.check_hostname = True
493498
ctx.verify_mode = ssl.CERT_REQUIRED
494499
info("LibreSSL-compatible configuration activated")
495-
500+
496501
elif ssl_version_info >= (3, 0, 0):
497-
# OpenSSL 3.x - use modern features
498-
ctx = ssl.create_default_context()
499502
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
500503
if hasattr(ssl, 'TLSVersion') and hasattr(ssl.TLSVersion, 'TLSv1_3'):
501504
ctx.maximum_version = ssl.TLSVersion.TLSv1_3
502505
info("OpenSSL 3.x modern configuration activated")
503-
506+
504507
elif ssl_version_info >= (1, 1, 1):
505-
# OpenSSL 1.1.1+ - proven configuration
506-
ctx = ssl.create_default_context()
507508
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
508509
info("OpenSSL 1.1.1+ standard configuration activated")
509-
510+
510511
else:
511-
# Legacy OpenSSL - basic functionality
512512
warn("Outdated OpenSSL version detected, using legacy mode")
513-
ctx = ssl.create_default_context()
514513
ctx.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
515-
514+
516515
# ESP-IDF DigiCert Certificate Handling
517516
parsed_url = urllib.parse.urlparse(url)
518517
domain = parsed_url.netloc.lower()
519-
518+
520519
if domain in DL_CERT_DICT:
521520
cert_data = DL_CERT_DICT[domain]
522521
ctx.load_verify_locations(cadata=cert_data)
523-
# Disable hostname checking for custom certificates
524522
ctx.check_hostname = False
525523
ctx.verify_mode = ssl.CERT_REQUIRED
526524
info(f"✓ Custom DigiCert certificate loaded for {domain}")
527-
528-
return ctx
529525

526+
return ctx
530527

531528
def get_ssl_fallback_contexts(url: str) -> List[Tuple[str, ssl.SSLContext]]:
532529
"""
533530
Creates fallback SSL contexts for different scenarios.
534-
531+
Incorporates certifi CA bundle if available.
532+
535533
This function provides multiple SSL context configurations that are tried
536534
in order when downloading fails. This approach maximizes compatibility
537535
across different systems and SSL configurations.
538-
536+
539537
Args:
540538
url: The URL to create contexts for
541-
539+
542540
Returns:
543541
List of tuples containing (config_name, ssl_context) pairs
544542
"""
545543
contexts = []
546-
547-
# 1. Primary context with backend detection
544+
545+
# 1. Primary context with backend detection and certifi support
548546
try:
549547
primary_ctx = create_esp_idf_ssl_context(url)
550548
contexts.append(('esp_idf_optimized', primary_ctx))
551549
except Exception as e:
552550
warn(f"Primary SSL context failed: {e}")
553-
554-
# 2. Standard context with custom certificates
551+
552+
# 2. Standard context with custom certificates and certifi support
555553
try:
556-
standard_ctx = ssl.create_default_context()
554+
cafile = get_certifi_path()
555+
standard_ctx = ssl.create_default_context(cafile=cafile) if cafile else ssl.create_default_context()
557556
parsed_url = urllib.parse.urlparse(url)
558557
domain = parsed_url.netloc.lower()
559-
558+
560559
if domain in DL_CERT_DICT:
561560
cert_data = DL_CERT_DICT[domain]
562561
standard_ctx.load_verify_locations(cadata=cert_data)
563562
standard_ctx.check_hostname = False
564-
563+
565564
contexts.append(('standard_with_custom_cert', standard_ctx))
566565
except Exception as e:
567566
warn(f"Standard SSL context with custom cert failed: {e}")
568-
569-
# 3. System default without modifications
567+
568+
# 3. System default
570569
try:
571570
system_ctx = ssl.create_default_context()
572571
contexts.append(('system_default', system_ctx))
573572
except Exception as e:
574573
warn(f"System default SSL context failed: {e}")
575-
574+
576575
# 4. Unverified as last resort
577576
try:
578577
unverified_ctx = ssl.create_default_context()
@@ -581,7 +580,7 @@ def get_ssl_fallback_contexts(url: str) -> List[Tuple[str, ssl.SSLContext]]:
581580
contexts.append(('unverified', unverified_ctx))
582581
except Exception as e:
583582
warn(f"Unverified SSL context failed: {e}")
584-
583+
585584
return contexts
586585

587586

0 commit comments

Comments
 (0)