mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
fix(mbedtls): Raise error for certificate files with unsupported extension
gen_crt_bundle.py only parses files ending in .pem or .der, but silently ignored anything else. A PEM certificate named e.g. ca.crt was skipped without a word, and since the build invokes the script with -q, even the "Successfully added 0 certificates" hint was suppressed. The build then succeeded and embedded a bundle without the certificate, and the problem only surfaced at runtime as a TLS verification failure. A file passed directly via --input, which is what CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH points at, is now expected to be a certificate: an unsupported extension raises an InputError and fails the build with a message naming the file and the two accepted extensions. Files found while scanning a directory keep being skipped, as a certificate directory may legitimately contain other files, but a warning is now printed unconditionally so it is visible in the build log. Also document the requirement in the Kconfig help text and in the esp_crt_bundle documentation. Closes https://github.com/espressif/esp-idf/issues/18933
This commit is contained in:
committed by
Mahavir Jain
parent
4ddc55b7a1
commit
d385023b85
@@ -516,6 +516,11 @@ menu "mbedTLS"
|
||||
Name of the custom certificate directory or file. This path is evaluated
|
||||
relative to the project root directory.
|
||||
|
||||
Certificates must be either PEM encoded with a .pem extension, or DER
|
||||
encoded with a .der extension. Files with any other extension are not
|
||||
parsed: pointing this option directly at such a file fails the build,
|
||||
while such files inside a directory are skipped with a warning.
|
||||
|
||||
config MBEDTLS_CERTIFICATE_BUNDLE_DEPRECATED_LIST
|
||||
bool "Add deprecated root certificates"
|
||||
depends on MBEDTLS_CERTIFICATE_BUNDLE && !MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE
|
||||
|
||||
@@ -46,6 +46,10 @@ except ImportError:
|
||||
|
||||
ca_bundle_bin_file = 'x509_crt_bundle'
|
||||
|
||||
# Only files with these extensions are parsed, the extension has to match the encoding of the file
|
||||
PEM_FILE_EXTENSION = '.pem'
|
||||
DER_FILE_EXTENSION = '.der'
|
||||
|
||||
quiet = False
|
||||
|
||||
|
||||
@@ -70,27 +74,44 @@ class CertificateBundle:
|
||||
def add_from_path(self, crts_path):
|
||||
found = False
|
||||
for file_path in os.listdir(crts_path):
|
||||
found |= self.add_from_file(os.path.join(crts_path, file_path))
|
||||
found |= self.add_from_file(os.path.join(crts_path, file_path), strict=False)
|
||||
|
||||
if found is False:
|
||||
raise InputError('No valid x509 certificates found in %s' % crts_path)
|
||||
|
||||
def add_from_file(self, file_path):
|
||||
def add_from_file(self, file_path, strict=True):
|
||||
"""Parse a certificate file
|
||||
|
||||
Only files with a .pem or .der extension are parsed. If strict is True (the file was
|
||||
given directly on the command line), any other extension is an error, otherwise
|
||||
(while scanning a directory) the file is skipped with a warning.
|
||||
"""
|
||||
try:
|
||||
if file_path.endswith('.pem'):
|
||||
if file_path.endswith(PEM_FILE_EXTENSION):
|
||||
status('Parsing certificates from %s' % file_path)
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
crt_str = f.read()
|
||||
self.add_from_pem(crt_str)
|
||||
return True
|
||||
|
||||
elif file_path.endswith('.der'):
|
||||
elif file_path.endswith(DER_FILE_EXTENSION):
|
||||
status('Parsing certificates from %s' % file_path)
|
||||
with open(file_path, 'rb') as f:
|
||||
crt_str = f.read()
|
||||
self.add_from_der(crt_str)
|
||||
return True
|
||||
|
||||
elif os.path.isfile(file_path):
|
||||
msg = (
|
||||
'Unsupported file extension in %s, certificates are only parsed from files with a '
|
||||
'%s (PEM encoded) or %s (DER encoded) extension. '
|
||||
'Please rename the file to match its encoding.'
|
||||
% (file_path, PEM_FILE_EXTENSION, DER_FILE_EXTENSION)
|
||||
)
|
||||
if strict:
|
||||
raise InputError(msg)
|
||||
critical('Warning: skipping file. %s' % msg)
|
||||
|
||||
except ValueError:
|
||||
critical('Invalid certificate in %s' % file_path)
|
||||
raise InputError('Invalid certificate')
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
try:
|
||||
@@ -22,6 +24,7 @@ verified_der_bundle = 'baltimore_crt_bundle'
|
||||
verified_pem_bundle = 'entrust_crt_bundle'
|
||||
invalid_test_file = 'invalid_crt.pem'
|
||||
non_ascii_file = 'non_ascii_crt.pem'
|
||||
unsupported_file = 'unsupported_ext_crt.crt'
|
||||
ca_crts_all_file = 'cacrt_all.pem'
|
||||
|
||||
|
||||
@@ -73,6 +76,37 @@ class GenCrtBundleTests(Py23TestCase):
|
||||
bundle.add_from_pem('')
|
||||
self.assertEqual(len(bundle.certificates), bundle_prev_len)
|
||||
|
||||
# A file given directly is expected to be a certificate, so an unknown extension is an error
|
||||
def test_unsupported_extension_input(self):
|
||||
bundle = gen_crt_bundle.CertificateBundle()
|
||||
|
||||
with self.assertRaisesRegex(gen_crt_bundle.InputError, 'Unsupported file extension'):
|
||||
bundle.add_from_file(test_crts_path + unsupported_file)
|
||||
|
||||
self.assertEqual(len(bundle.certificates), 0)
|
||||
|
||||
# While scanning a directory, files with an unknown extension are skipped instead
|
||||
def test_unsupported_extension_in_dir(self):
|
||||
bundle = gen_crt_bundle.CertificateBundle()
|
||||
|
||||
self.assertFalse(bundle.add_from_file(test_crts_path + unsupported_file, strict=False))
|
||||
self.assertEqual(len(bundle.certificates), 0)
|
||||
|
||||
# A directory holding both valid and unsupported files adds only the valid ones
|
||||
with tempfile.TemporaryDirectory() as crts_dir:
|
||||
shutil.copy(test_crts_path + unsupported_file, crts_dir)
|
||||
shutil.copy(test_crts_path + pem_test_file, crts_dir)
|
||||
|
||||
bundle.add_from_path(crts_dir)
|
||||
self.assertEqual(len(bundle.certificates), 1)
|
||||
|
||||
# A directory without any supported file is an error
|
||||
with tempfile.TemporaryDirectory() as crts_dir:
|
||||
shutil.copy(test_crts_path + unsupported_file, crts_dir)
|
||||
|
||||
with self.assertRaisesRegex(gen_crt_bundle.InputError, 'No valid x509 certificates found'):
|
||||
bundle.add_from_path(crts_dir)
|
||||
|
||||
def test_non_ascii_crt_input(self):
|
||||
bundle = gen_crt_bundle.CertificateBundle()
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
Entrust Root Certification Authority
|
||||
====================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV
|
||||
BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw
|
||||
b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG
|
||||
A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0
|
||||
MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu
|
||||
MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu
|
||||
Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v
|
||||
dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||
ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz
|
||||
A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww
|
||||
Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68
|
||||
j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN
|
||||
rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw
|
||||
DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1
|
||||
MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH
|
||||
hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
|
||||
A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM
|
||||
Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa
|
||||
v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS
|
||||
W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
|
||||
tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
|
||||
-----END CERTIFICATE-----
|
||||
@@ -30,6 +30,10 @@ Most configuration is done through menuconfig. CMake generates the bundle accord
|
||||
* :menuitem:`CONFIG_MBEDTLS_DEFAULT_CERTIFICATE_BUNDLE`: decide which certificates to include from the complete root certificate list.
|
||||
* :menuitem:`CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH`: specify the path of any additional certificates to embed in the bundle.
|
||||
|
||||
.. note::
|
||||
|
||||
Only PEM encoded certificates with a ``.pem`` extension and DER encoded certificates with a ``.der`` extension are parsed. The extension must match the encoding of the file, for example a PEM encoded certificate saved as ``.crt`` is not accepted. If :ref:`CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH` points directly at a file with any other extension, the build fails; if such a file is found inside a certificate directory, it is skipped and a warning is printed.
|
||||
|
||||
To enable the bundle when using ESP-TLS simply pass the function pointer to the bundle attach function:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@@ -30,6 +30,10 @@ ESP x509 证书包 API 提供了一种简便的方法,帮助你安装自定义
|
||||
* :menuitem:`CONFIG_MBEDTLS_DEFAULT_CERTIFICATE_BUNDLE`:决定添加证书列表中的哪些证书。
|
||||
* :menuitem:`CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH`:指定要在证书包中嵌入的其他证书的路径。
|
||||
|
||||
.. note::
|
||||
|
||||
只有扩展名为 ``.pem`` 的 PEM 编码证书和扩展名为 ``.der`` 的 DER 编码证书会被解析。扩展名必须与文件的编码格式一致,例如保存为 ``.crt`` 的 PEM 编码证书不会被接受。如果 :ref:`CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH` 直接指向其他扩展名的文件,构建将失败;如果证书目录中存在此类文件,该文件会被跳过并打印警告。
|
||||
|
||||
要在使用 ESP-TLS 时启用证书包,将函数指针指向证书包的 attach 函数:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
Reference in New Issue
Block a user