From ea4977691810877af46824940f7678e0759a1b3c Mon Sep 17 00:00:00 2001 From: Shane Harvey Date: Fri, 2 Aug 2024 18:22:30 -0700 Subject: [PATCH 1/3] PYTHON-4611 Prefer non deprecated cryptography apis --- pymongo/ocsp_cache.py | 49 +++++++++++++++++++++++++++-------------- pymongo/ocsp_support.py | 12 +++++++--- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/pymongo/ocsp_cache.py b/pymongo/ocsp_cache.py index 742579312f..caf836c06f 100644 --- a/pymongo/ocsp_cache.py +++ b/pymongo/ocsp_cache.py @@ -19,7 +19,7 @@ from collections import namedtuple from datetime import datetime as _datetime from datetime import timezone -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Optional from pymongo.lock import _create_lock @@ -27,6 +27,20 @@ from cryptography.x509.ocsp import OCSPRequest, OCSPResponse +def _next_update(value: OCSPResponse) -> Optional[_datetime]: + """Compat helper to return the response's next_update_utc.""" + if hasattr(value, "next_update_utc"): + return value.next_update_utc + return value.next_update + + +def _this_update(value: OCSPResponse) -> Optional[_datetime]: + """Compat helper to return the response's this_update_utc.""" + if hasattr(value, "this_update_utc"): + return value.this_update_utc + return value.this_update + + class _OCSPCache: """A cache for OCSP responses.""" @@ -62,24 +76,25 @@ def __setitem__(self, key: OCSPRequest, value: OCSPResponse) -> None: # As per the OCSP protocol, if the response's nextUpdate field is # not set, the responder is indicating that newer revocation # information is available all the time. - if value.next_update is None: + next_update = _next_update(value) + if next_update is None: self._data.pop(cache_key, None) return + this_update = _this_update(value) + now = _datetime.now(tz=timezone.utc) + if this_update.tzinfo is None: + # Make naive to match cryptography. + now = now.replace(tzinfo=None) # Do nothing if the response is invalid. - if not ( - value.this_update - <= _datetime.now(tz=timezone.utc).replace(tzinfo=None) - < value.next_update - ): + if not this_update <= now < next_update: return # Cache new response OR update cached response if new response # has longer validity. cached_value = self._data.get(cache_key, None) if cached_value is None or ( - cached_value.next_update is not None - and cached_value.next_update < value.next_update + _next_update(cached_value) is not None and _next_update(cached_value) < next_update ): self._data[cache_key] = value @@ -95,13 +110,15 @@ def __getitem__(self, item: OCSPRequest) -> OCSPResponse: value = self._data[cache_key] # Return cached response if it is still valid. - assert value.this_update is not None - assert value.next_update is not None - if ( - value.this_update - <= _datetime.now(tz=timezone.utc).replace(tzinfo=None) - < value.next_update - ): + this_update = _this_update(value) + next_update = _next_update(value) + now = _datetime.now(tz=timezone.utc) + if this_update.tzinfo is None: + # Make naive to match cryptography. + now = now.replace(tzinfo=None) + assert this_update is not None + assert next_update is not None + if this_update <= now < next_update: return value self._data.pop(cache_key, None) diff --git a/pymongo/ocsp_support.py b/pymongo/ocsp_support.py index 1bda3b4d71..aaa16e7ca9 100644 --- a/pymongo/ocsp_support.py +++ b/pymongo/ocsp_support.py @@ -58,6 +58,7 @@ from requests.exceptions import RequestException as _RequestException from pymongo import _csot +from pymongo.ocsp_cache import _next_update, _this_update if TYPE_CHECKING: from cryptography.hazmat.primitives.asymmetric import ( @@ -275,13 +276,18 @@ def _verify_response(issuer: Certificate, response: OCSPResponse) -> int: # Note that we are not using a "tolerance period" as discussed in # https://tools.ietf.org/rfc/rfc5019.txt? - now = _datetime.now(tz=timezone.utc).replace(tzinfo=None) + this_update = _this_update(response) + now = _datetime.now(tz=timezone.utc) + if this_update.tzinfo is None: + # Make naive to match cryptography. + now = now.replace(tzinfo=None) # RFC6960, Section 3.2, Number 5 - if response.this_update > now: + if this_update > now: _LOGGER.debug("thisUpdate is in the future") return 0 # RFC6960, Section 3.2, Number 6 - if response.next_update and response.next_update < now: + next_update = _next_update(response) + if next_update and next_update < now: _LOGGER.debug("nextUpdate is in the past") return 0 return 1 From 6e2184b5f4a5fbc5c36c7efe0261f01fff16bd8c Mon Sep 17 00:00:00 2001 From: Shane Harvey Date: Fri, 2 Aug 2024 18:33:45 -0700 Subject: [PATCH 2/3] PYTHON-4611 Fix type checking --- pymongo/ocsp_cache.py | 14 ++++++++++---- pymongo/ocsp_support.py | 4 ++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pymongo/ocsp_cache.py b/pymongo/ocsp_cache.py index caf836c06f..bc1537020c 100644 --- a/pymongo/ocsp_cache.py +++ b/pymongo/ocsp_cache.py @@ -82,20 +82,24 @@ def __setitem__(self, key: OCSPRequest, value: OCSPResponse) -> None: return this_update = _this_update(value) + if this_update is None: + return now = _datetime.now(tz=timezone.utc) if this_update.tzinfo is None: # Make naive to match cryptography. now = now.replace(tzinfo=None) # Do nothing if the response is invalid. - if not this_update <= now < next_update: + if not (this_update <= now < next_update): return # Cache new response OR update cached response if new response # has longer validity. cached_value = self._data.get(cache_key, None) - if cached_value is None or ( - _next_update(cached_value) is not None and _next_update(cached_value) < next_update - ): + if cached_value is None: + self._data[cache_key] = value + return + cached_next_update = _next_update(cached_value) + if cached_next_update is not None and cached_next_update < next_update: self._data[cache_key] = value def __getitem__(self, item: OCSPRequest) -> OCSPResponse: @@ -112,6 +116,8 @@ def __getitem__(self, item: OCSPRequest) -> OCSPResponse: # Return cached response if it is still valid. this_update = _this_update(value) next_update = _next_update(value) + assert this_update is not None + assert next_update is not None now = _datetime.now(tz=timezone.utc) if this_update.tzinfo is None: # Make naive to match cryptography. diff --git a/pymongo/ocsp_support.py b/pymongo/ocsp_support.py index aaa16e7ca9..ee359b71c2 100644 --- a/pymongo/ocsp_support.py +++ b/pymongo/ocsp_support.py @@ -278,11 +278,11 @@ def _verify_response(issuer: Certificate, response: OCSPResponse) -> int: # https://tools.ietf.org/rfc/rfc5019.txt? this_update = _this_update(response) now = _datetime.now(tz=timezone.utc) - if this_update.tzinfo is None: + if this_update and this_update.tzinfo is None: # Make naive to match cryptography. now = now.replace(tzinfo=None) # RFC6960, Section 3.2, Number 5 - if this_update > now: + if this_update and this_update > now: _LOGGER.debug("thisUpdate is in the future") return 0 # RFC6960, Section 3.2, Number 6 From e7aa7f6ed0b3a1c198eb7df6e369e7fbd74a84fa Mon Sep 17 00:00:00 2001 From: Shane Harvey Date: Mon, 5 Aug 2024 11:01:04 -0700 Subject: [PATCH 3/3] PYTHON-4611 Remove duplicate asserts --- pymongo/ocsp_cache.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pymongo/ocsp_cache.py b/pymongo/ocsp_cache.py index bc1537020c..3facefe350 100644 --- a/pymongo/ocsp_cache.py +++ b/pymongo/ocsp_cache.py @@ -29,6 +29,7 @@ def _next_update(value: OCSPResponse) -> Optional[_datetime]: """Compat helper to return the response's next_update_utc.""" + # Added in cryptography 43.0.0. if hasattr(value, "next_update_utc"): return value.next_update_utc return value.next_update @@ -36,6 +37,7 @@ def _next_update(value: OCSPResponse) -> Optional[_datetime]: def _this_update(value: OCSPResponse) -> Optional[_datetime]: """Compat helper to return the response's this_update_utc.""" + # Added in cryptography 43.0.0. if hasattr(value, "this_update_utc"): return value.this_update_utc return value.this_update @@ -122,8 +124,6 @@ def __getitem__(self, item: OCSPRequest) -> OCSPResponse: if this_update.tzinfo is None: # Make naive to match cryptography. now = now.replace(tzinfo=None) - assert this_update is not None - assert next_update is not None if this_update <= now < next_update: return value