Skip to content

Commit 0129fb0

Browse files
committed
Upgrade vendored truststore to 0.9.0
1 parent e884c00 commit 0129fb0

File tree

6 files changed

+52
-36
lines changed

6 files changed

+52
-36
lines changed

news/truststore.vendor.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Upgrade truststore to 0.9.0

src/pip/_vendor/truststore/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010
del _api, _sys # type: ignore[name-defined] # noqa: F821
1111

1212
__all__ = ["SSLContext", "inject_into_ssl", "extract_from_ssl"]
13-
__version__ = "0.8.0"
13+
__version__ = "0.9.0"

src/pip/_vendor/truststore/_api.py

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
import platform
33
import socket
44
import ssl
5+
import sys
56
import typing
67

7-
import _ssl # type: ignore[import]
8-
98
from ._ssl_constants import (
109
_original_SSLContext,
1110
_original_super_SSLContext,
@@ -49,7 +48,7 @@ def extract_from_ssl() -> None:
4948
try:
5049
import pip._vendor.urllib3.util.ssl_ as urllib3_ssl
5150

52-
urllib3_ssl.SSLContext = _original_SSLContext
51+
urllib3_ssl.SSLContext = _original_SSLContext # type: ignore[assignment]
5352
except ImportError:
5453
pass
5554

@@ -171,16 +170,13 @@ def cert_store_stats(self) -> dict[str, int]:
171170
@typing.overload
172171
def get_ca_certs(
173172
self, binary_form: typing.Literal[False] = ...
174-
) -> list[typing.Any]:
175-
...
173+
) -> list[typing.Any]: ...
176174

177175
@typing.overload
178-
def get_ca_certs(self, binary_form: typing.Literal[True] = ...) -> list[bytes]:
179-
...
176+
def get_ca_certs(self, binary_form: typing.Literal[True] = ...) -> list[bytes]: ...
180177

181178
@typing.overload
182-
def get_ca_certs(self, binary_form: bool = ...) -> typing.Any:
183-
...
179+
def get_ca_certs(self, binary_form: bool = ...) -> typing.Any: ...
184180

185181
def get_ca_certs(self, binary_form: bool = False) -> list[typing.Any] | list[bytes]:
186182
raise NotImplementedError()
@@ -276,6 +272,23 @@ def verify_mode(self, value: ssl.VerifyMode) -> None:
276272
)
277273

278274

275+
# Python 3.13+ makes get_unverified_chain() a public API that only returns DER
276+
# encoded certificates. We detect whether we need to call public_bytes() for 3.10->3.12
277+
# Pre-3.13 returned None instead of an empty list from get_unverified_chain()
278+
if sys.version_info >= (3, 13):
279+
280+
def _get_unverified_chain_bytes(sslobj: ssl.SSLObject) -> list[bytes]:
281+
unverified_chain = sslobj.get_unverified_chain() or () # type: ignore[attr-defined]
282+
return [cert for cert in unverified_chain]
283+
284+
else:
285+
import _ssl # type: ignore[import-not-found]
286+
287+
def _get_unverified_chain_bytes(sslobj: ssl.SSLObject) -> list[bytes]:
288+
unverified_chain = sslobj.get_unverified_chain() or () # type: ignore[attr-defined]
289+
return [cert.public_bytes(_ssl.ENCODING_DER) for cert in unverified_chain]
290+
291+
279292
def _verify_peercerts(
280293
sock_or_sslobj: ssl.SSLSocket | ssl.SSLObject, server_hostname: str | None
281294
) -> None:
@@ -290,13 +303,7 @@ def _verify_peercerts(
290303
except AttributeError:
291304
pass
292305

293-
# SSLObject.get_unverified_chain() returns 'None'
294-
# if the peer sends no certificates. This is common
295-
# for the server-side scenario.
296-
unverified_chain: typing.Sequence[_ssl.Certificate] = (
297-
sslobj.get_unverified_chain() or () # type: ignore[attr-defined]
298-
)
299-
cert_bytes = [cert.public_bytes(_ssl.ENCODING_DER) for cert in unverified_chain]
306+
cert_bytes = _get_unverified_chain_bytes(sslobj)
300307
_verify_peercerts_impl(
301308
sock_or_sslobj.context, cert_bytes, server_hostname=server_hostname
302309
)

src/pip/_vendor/truststore/_macos.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,6 @@ def _load_cdll(name: str, macos10_16_path: str) -> CDLL:
9696
Security.SecTrustSetAnchorCertificatesOnly.argtypes = [SecTrustRef, Boolean]
9797
Security.SecTrustSetAnchorCertificatesOnly.restype = OSStatus
9898

99-
Security.SecTrustEvaluate.argtypes = [SecTrustRef, POINTER(SecTrustResultType)]
100-
Security.SecTrustEvaluate.restype = OSStatus
101-
10299
Security.SecPolicyCreateRevocation.argtypes = [CFOptionFlags]
103100
Security.SecPolicyCreateRevocation.restype = SecPolicyRef
104101

@@ -259,6 +256,7 @@ def _handle_osstatus(result: OSStatus, _: typing.Any, args: typing.Any) -> typin
259256

260257
Security.SecTrustCreateWithCertificates.errcheck = _handle_osstatus # type: ignore[assignment]
261258
Security.SecTrustSetAnchorCertificates.errcheck = _handle_osstatus # type: ignore[assignment]
259+
Security.SecTrustSetAnchorCertificatesOnly.errcheck = _handle_osstatus # type: ignore[assignment]
262260
Security.SecTrustGetTrustResult.errcheck = _handle_osstatus # type: ignore[assignment]
263261

264262

@@ -417,21 +415,21 @@ def _verify_peercerts_impl(
417415
CoreFoundation.CFRelease(certs)
418416

419417
# If there are additional trust anchors to load we need to transform
420-
# the list of DER-encoded certificates into a CFArray. Otherwise
421-
# pass 'None' to signal that we only want system / fetched certificates.
418+
# the list of DER-encoded certificates into a CFArray.
422419
ctx_ca_certs_der: list[bytes] | None = ssl_context.get_ca_certs(
423420
binary_form=True
424421
)
425422
if ctx_ca_certs_der:
426423
ctx_ca_certs = None
427424
try:
428-
ctx_ca_certs = _der_certs_to_cf_cert_array(cert_chain)
425+
ctx_ca_certs = _der_certs_to_cf_cert_array(ctx_ca_certs_der)
429426
Security.SecTrustSetAnchorCertificates(trust, ctx_ca_certs)
430427
finally:
431428
if ctx_ca_certs:
432429
CoreFoundation.CFRelease(ctx_ca_certs)
433-
else:
434-
Security.SecTrustSetAnchorCertificates(trust, None)
430+
431+
# We always want system certificates.
432+
Security.SecTrustSetAnchorCertificatesOnly(trust, False)
435433

436434
cf_error = CoreFoundation.CFErrorRef()
437435
sec_trust_eval_result = Security.SecTrustEvaluateWithError(

src/pip/_vendor/truststore/_windows.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,12 @@ def _verify_peercerts_impl(
325325
server_hostname: str | None = None,
326326
) -> None:
327327
"""Verify the cert_chain from the server using Windows APIs."""
328+
329+
# If the peer didn't send any certificates then
330+
# we can't do verification. Raise an error.
331+
if not cert_chain:
332+
raise ssl.SSLCertVerificationError("Peer sent no certificates to verify")
333+
328334
pCertContext = None
329335
hIntermediateCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, None, 0, None)
330336
try:
@@ -375,7 +381,7 @@ def _verify_peercerts_impl(
375381
server_hostname,
376382
chain_flags=chain_flags,
377383
)
378-
except ssl.SSLCertVerificationError:
384+
except ssl.SSLCertVerificationError as e:
379385
# If that fails but custom CA certs have been added
380386
# to the SSLContext using load_verify_locations,
381387
# try verifying using a custom chain engine
@@ -384,15 +390,19 @@ def _verify_peercerts_impl(
384390
binary_form=True
385391
)
386392
if custom_ca_certs:
387-
_verify_using_custom_ca_certs(
388-
ssl_context,
389-
custom_ca_certs,
390-
hIntermediateCertStore,
391-
pCertContext,
392-
pChainPara,
393-
server_hostname,
394-
chain_flags=chain_flags,
395-
)
393+
try:
394+
_verify_using_custom_ca_certs(
395+
ssl_context,
396+
custom_ca_certs,
397+
hIntermediateCertStore,
398+
pCertContext,
399+
pChainPara,
400+
server_hostname,
401+
chain_flags=chain_flags,
402+
)
403+
# Raise the original error, not the new error.
404+
except ssl.SSLCertVerificationError:
405+
raise e from None
396406
else:
397407
raise
398408
finally:

src/pip/_vendor/vendor.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ setuptools==69.1.1
2020
six==1.16.0
2121
tenacity==8.2.3
2222
tomli==2.0.1
23-
truststore==0.8.0
23+
truststore==0.9.0
2424
webencodings==0.5.1

0 commit comments

Comments
 (0)