2
2
import platform
3
3
import socket
4
4
import ssl
5
+ import sys
5
6
import typing
6
7
7
- import _ssl # type: ignore[import]
8
+ import _ssl # type: ignore[import-not-found ]
8
9
9
10
from ._ssl_constants import (
10
11
_original_SSLContext ,
@@ -49,7 +50,7 @@ def extract_from_ssl() -> None:
49
50
try :
50
51
import pip ._vendor .urllib3 .util .ssl_ as urllib3_ssl
51
52
52
- urllib3_ssl .SSLContext = _original_SSLContext
53
+ urllib3_ssl .SSLContext = _original_SSLContext # type: ignore[assignment]
53
54
except ImportError :
54
55
pass
55
56
@@ -171,16 +172,13 @@ def cert_store_stats(self) -> dict[str, int]:
171
172
@typing .overload
172
173
def get_ca_certs (
173
174
self , binary_form : typing .Literal [False ] = ...
174
- ) -> list [typing .Any ]:
175
- ...
175
+ ) -> list [typing .Any ]: ...
176
176
177
177
@typing .overload
178
- def get_ca_certs (self , binary_form : typing .Literal [True ] = ...) -> list [bytes ]:
179
- ...
178
+ def get_ca_certs (self , binary_form : typing .Literal [True ] = ...) -> list [bytes ]: ...
180
179
181
180
@typing .overload
182
- def get_ca_certs (self , binary_form : bool = ...) -> typing .Any :
183
- ...
181
+ def get_ca_certs (self , binary_form : bool = ...) -> typing .Any : ...
184
182
185
183
def get_ca_certs (self , binary_form : bool = False ) -> list [typing .Any ] | list [bytes ]:
186
184
raise NotImplementedError ()
@@ -276,6 +274,25 @@ def verify_mode(self, value: ssl.VerifyMode) -> None:
276
274
)
277
275
278
276
277
+ # Python 3.13+ makes get_unverified_chain() a public API that only returns DER
278
+ # encoded certificates. We detect whether we need to call public_bytes() for 3.10->3.12
279
+ # Pre-3.13 returned None instead of an empty list from get_unverified_chain()
280
+ if sys .version_info >= (3 , 13 ):
281
+
282
+ def _get_unverified_chain_bytes (sslobj : ssl .SSLObject ) -> list [bytes ]:
283
+ unverified_chain = sslobj .get_unverified_chain () or () # type: ignore[attr-defined]
284
+ return [
285
+ cert if isinstance (cert , bytes ) else cert .public_bytes (_ssl .ENCODING_DER )
286
+ for cert in unverified_chain
287
+ ]
288
+
289
+ else :
290
+
291
+ def _get_unverified_chain_bytes (sslobj : ssl .SSLObject ) -> list [bytes ]:
292
+ unverified_chain = sslobj .get_unverified_chain () or () # type: ignore[attr-defined]
293
+ return [cert .public_bytes (_ssl .ENCODING_DER ) for cert in unverified_chain ]
294
+
295
+
279
296
def _verify_peercerts (
280
297
sock_or_sslobj : ssl .SSLSocket | ssl .SSLObject , server_hostname : str | None
281
298
) -> None :
@@ -290,13 +307,7 @@ def _verify_peercerts(
290
307
except AttributeError :
291
308
pass
292
309
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 ]
310
+ cert_bytes = _get_unverified_chain_bytes (sslobj )
300
311
_verify_peercerts_impl (
301
312
sock_or_sslobj .context , cert_bytes , server_hostname = server_hostname
302
313
)
0 commit comments