diff --git a/news/truststore.vendor.rst b/news/truststore.vendor.rst deleted file mode 100644 index a18235085f9..00000000000 --- a/news/truststore.vendor.rst +++ /dev/null @@ -1 +0,0 @@ -Upgrade truststore to 0.9.0 diff --git a/src/pip/_vendor/truststore/__init__.py b/src/pip/_vendor/truststore/__init__.py index 9e7f26795fc..59930f455b0 100644 --- a/src/pip/_vendor/truststore/__init__.py +++ b/src/pip/_vendor/truststore/__init__.py @@ -10,4 +10,4 @@ del _api, _sys # type: ignore[name-defined] # noqa: F821 __all__ = ["SSLContext", "inject_into_ssl", "extract_from_ssl"] -__version__ = "0.9.0" +__version__ = "0.8.0" diff --git a/src/pip/_vendor/truststore/_api.py b/src/pip/_vendor/truststore/_api.py index f1ac6676813..829aff72672 100644 --- a/src/pip/_vendor/truststore/_api.py +++ b/src/pip/_vendor/truststore/_api.py @@ -2,9 +2,10 @@ import platform import socket import ssl -import sys import typing +import _ssl # type: ignore[import] + from ._ssl_constants import ( _original_SSLContext, _original_super_SSLContext, @@ -48,7 +49,7 @@ def extract_from_ssl() -> None: try: import pip._vendor.urllib3.util.ssl_ as urllib3_ssl - urllib3_ssl.SSLContext = _original_SSLContext # type: ignore[assignment] + urllib3_ssl.SSLContext = _original_SSLContext except ImportError: pass @@ -170,13 +171,16 @@ def cert_store_stats(self) -> dict[str, int]: @typing.overload def get_ca_certs( self, binary_form: typing.Literal[False] = ... - ) -> list[typing.Any]: ... + ) -> list[typing.Any]: + ... @typing.overload - def get_ca_certs(self, binary_form: typing.Literal[True] = ...) -> list[bytes]: ... + def get_ca_certs(self, binary_form: typing.Literal[True] = ...) -> list[bytes]: + ... @typing.overload - def get_ca_certs(self, binary_form: bool = ...) -> typing.Any: ... + def get_ca_certs(self, binary_form: bool = ...) -> typing.Any: + ... def get_ca_certs(self, binary_form: bool = False) -> list[typing.Any] | list[bytes]: raise NotImplementedError() @@ -272,23 +276,6 @@ def verify_mode(self, value: ssl.VerifyMode) -> None: ) -# Python 3.13+ makes get_unverified_chain() a public API that only returns DER -# encoded certificates. We detect whether we need to call public_bytes() for 3.10->3.12 -# Pre-3.13 returned None instead of an empty list from get_unverified_chain() -if sys.version_info >= (3, 13): - - def _get_unverified_chain_bytes(sslobj: ssl.SSLObject) -> list[bytes]: - unverified_chain = sslobj.get_unverified_chain() or () # type: ignore[attr-defined] - return [cert for cert in unverified_chain] - -else: - import _ssl # type: ignore[import-not-found] - - def _get_unverified_chain_bytes(sslobj: ssl.SSLObject) -> list[bytes]: - unverified_chain = sslobj.get_unverified_chain() or () # type: ignore[attr-defined] - return [cert.public_bytes(_ssl.ENCODING_DER) for cert in unverified_chain] - - def _verify_peercerts( sock_or_sslobj: ssl.SSLSocket | ssl.SSLObject, server_hostname: str | None ) -> None: @@ -303,7 +290,13 @@ def _verify_peercerts( except AttributeError: pass - cert_bytes = _get_unverified_chain_bytes(sslobj) + # SSLObject.get_unverified_chain() returns 'None' + # if the peer sends no certificates. This is common + # for the server-side scenario. + unverified_chain: typing.Sequence[_ssl.Certificate] = ( + sslobj.get_unverified_chain() or () # type: ignore[attr-defined] + ) + cert_bytes = [cert.public_bytes(_ssl.ENCODING_DER) for cert in unverified_chain] _verify_peercerts_impl( sock_or_sslobj.context, cert_bytes, server_hostname=server_hostname ) diff --git a/src/pip/_vendor/truststore/_macos.py b/src/pip/_vendor/truststore/_macos.py index b234ffec723..7dc440bf362 100644 --- a/src/pip/_vendor/truststore/_macos.py +++ b/src/pip/_vendor/truststore/_macos.py @@ -96,6 +96,9 @@ def _load_cdll(name: str, macos10_16_path: str) -> CDLL: Security.SecTrustSetAnchorCertificatesOnly.argtypes = [SecTrustRef, Boolean] Security.SecTrustSetAnchorCertificatesOnly.restype = OSStatus + Security.SecTrustEvaluate.argtypes = [SecTrustRef, POINTER(SecTrustResultType)] + Security.SecTrustEvaluate.restype = OSStatus + Security.SecPolicyCreateRevocation.argtypes = [CFOptionFlags] Security.SecPolicyCreateRevocation.restype = SecPolicyRef @@ -256,7 +259,6 @@ def _handle_osstatus(result: OSStatus, _: typing.Any, args: typing.Any) -> typin Security.SecTrustCreateWithCertificates.errcheck = _handle_osstatus # type: ignore[assignment] Security.SecTrustSetAnchorCertificates.errcheck = _handle_osstatus # type: ignore[assignment] -Security.SecTrustSetAnchorCertificatesOnly.errcheck = _handle_osstatus # type: ignore[assignment] Security.SecTrustGetTrustResult.errcheck = _handle_osstatus # type: ignore[assignment] @@ -415,21 +417,21 @@ def _verify_peercerts_impl( CoreFoundation.CFRelease(certs) # If there are additional trust anchors to load we need to transform - # the list of DER-encoded certificates into a CFArray. + # the list of DER-encoded certificates into a CFArray. Otherwise + # pass 'None' to signal that we only want system / fetched certificates. ctx_ca_certs_der: list[bytes] | None = ssl_context.get_ca_certs( binary_form=True ) if ctx_ca_certs_der: ctx_ca_certs = None try: - ctx_ca_certs = _der_certs_to_cf_cert_array(ctx_ca_certs_der) + ctx_ca_certs = _der_certs_to_cf_cert_array(cert_chain) Security.SecTrustSetAnchorCertificates(trust, ctx_ca_certs) finally: if ctx_ca_certs: CoreFoundation.CFRelease(ctx_ca_certs) - - # We always want system certificates. - Security.SecTrustSetAnchorCertificatesOnly(trust, False) + else: + Security.SecTrustSetAnchorCertificates(trust, None) cf_error = CoreFoundation.CFErrorRef() sec_trust_eval_result = Security.SecTrustEvaluateWithError( diff --git a/src/pip/_vendor/truststore/_windows.py b/src/pip/_vendor/truststore/_windows.py index 3d00d467f99..3de4960a1b0 100644 --- a/src/pip/_vendor/truststore/_windows.py +++ b/src/pip/_vendor/truststore/_windows.py @@ -325,12 +325,6 @@ def _verify_peercerts_impl( server_hostname: str | None = None, ) -> None: """Verify the cert_chain from the server using Windows APIs.""" - - # If the peer didn't send any certificates then - # we can't do verification. Raise an error. - if not cert_chain: - raise ssl.SSLCertVerificationError("Peer sent no certificates to verify") - pCertContext = None hIntermediateCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, None, 0, None) try: @@ -381,7 +375,7 @@ def _verify_peercerts_impl( server_hostname, chain_flags=chain_flags, ) - except ssl.SSLCertVerificationError as e: + except ssl.SSLCertVerificationError: # If that fails but custom CA certs have been added # to the SSLContext using load_verify_locations, # try verifying using a custom chain engine @@ -390,19 +384,15 @@ def _verify_peercerts_impl( binary_form=True ) if custom_ca_certs: - try: - _verify_using_custom_ca_certs( - ssl_context, - custom_ca_certs, - hIntermediateCertStore, - pCertContext, - pChainPara, - server_hostname, - chain_flags=chain_flags, - ) - # Raise the original error, not the new error. - except ssl.SSLCertVerificationError: - raise e from None + _verify_using_custom_ca_certs( + ssl_context, + custom_ca_certs, + hIntermediateCertStore, + pCertContext, + pChainPara, + server_hostname, + chain_flags=chain_flags, + ) else: raise finally: diff --git a/src/pip/_vendor/vendor.txt b/src/pip/_vendor/vendor.txt index 14166a8192d..f00a8c02db4 100644 --- a/src/pip/_vendor/vendor.txt +++ b/src/pip/_vendor/vendor.txt @@ -20,5 +20,5 @@ setuptools==69.1.1 six==1.16.0 tenacity==8.2.3 tomli==2.0.1 -truststore==0.9.0 +truststore==0.8.0 webencodings==0.5.1