From 081ebcf1e7776b4a67e3c350020068d66ce0e416 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 20 May 2020 09:50:57 +0200 Subject: [PATCH] tools: add dl.espressif.com root cert into idf_tools.py Closes https://github.com/espressif/esp-idf/issues/5842 --- tools/idf_tools.py | 94 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 3 deletions(-) diff --git a/tools/idf_tools.py b/tools/idf_tools.py index 3525d29be4..d5683fa8f0 100755 --- a/tools/idf_tools.py +++ b/tools/idf_tools.py @@ -55,6 +55,8 @@ import shutil import functools import copy from collections import OrderedDict, namedtuple +import ssl +import contextlib try: import typing # noqa: F401 @@ -62,9 +64,11 @@ except ImportError: pass try: - from urllib.request import urlretrieve + from urllib.parse import splittype + from urllib.request import urlopen + from urllib.error import ContentTooShortError except ImportError: - from urllib import urlretrieve + from urllib import urlopen, splittype, ContentTooShortError try: from exceptions import WindowsError @@ -139,6 +143,40 @@ CURRENT_PLATFORM = PLATFORM_FROM_NAME.get(PYTHON_PLATFORM, UNKNOWN_PLATFORM) EXPORT_SHELL = 'shell' EXPORT_KEY_VALUE = 'key-value' +ISRG_X1_ROOT_CERT = """ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +""" + global_quiet = False global_non_interactive = False @@ -265,6 +303,49 @@ def unpack(filename, destination): archive_obj.extractall(destination) +# An alternative version of urlretrieve which takes SSL context as an argument +def urlretrieve_ctx(url, filename, reporthook=None, data=None, context=None): + url_type, path = splittype(url) + + with contextlib.closing(urlopen(url, data, context=context)) as fp: + headers = fp.info() + + # Just return the local path and the "headers" for file:// + # URLs. No sense in performing a copy unless requested. + if url_type == "file" and not filename: + return os.path.normpath(path), headers + + # Handle temporary file setup. + tfp = open(filename, 'wb') + + with tfp: + result = filename, headers + bs = 1024 * 8 + size = int(headers.get("content-length", -1)) + read = 0 + blocknum = 0 + + if reporthook: + reporthook(blocknum, bs, size) + + while True: + block = fp.read(bs) + if not block: + break + read += len(block) + tfp.write(block) + blocknum += 1 + if reporthook: + reporthook(blocknum, bs, size) + + if size >= 0 and read < size: + raise ContentTooShortError( + "retrieval incomplete: got only %i out of %i bytes" + % (read, size), result) + + return result + + # Sometimes renaming a directory on Windows (randomly?) causes a PermissionError. # This is confirmed to be a workaround: # https://github.com/espressif/esp-idf/issues/3819#issuecomment-515167118 @@ -580,7 +661,14 @@ class IDFTool(object): local_temp_path = local_path + '.tmp' info('Downloading {} to {}'.format(archive_name, local_temp_path)) try: - urlretrieve(url, local_temp_path, report_progress if not global_non_interactive else None) + ctx = None + # For dl.espressif.com, add the ISRG x1 root certificate. + # This works around the issue with outdated certificate stores in some installations. + if "dl.espressif.com" in url: + ctx = ssl.create_default_context() + ctx.load_verify_locations(cadata=ISRG_X1_ROOT_CERT) + + urlretrieve_ctx(url, local_temp_path, report_progress if not global_non_interactive else None, context=ctx) sys.stdout.write("\rDone\n") except Exception as e: # urlretrieve could throw different exceptions, e.g. IOError when the server is down