From afac304821e3fcbc76a42ea44d0b1ada26dddc37 Mon Sep 17 00:00:00 2001 From: Dpeta Date: Thu, 22 Dec 2022 22:03:26 +0100 Subject: [PATCH] Prefer certifi provided certificates (#108) Sets the SSL context to use the certifi provided root certificate bundle by default, unless the certifi module is older than a year. If the system-provided root certificate bundle is empty, certifi certs are always loaded. --- oyoyo/client.py | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/oyoyo/client.py b/oyoyo/client.py index e831bf0..e5f1689 100644 --- a/oyoyo/client.py +++ b/oyoyo/client.py @@ -21,6 +21,7 @@ import ssl import socket import select import logging +import datetime import traceback from oyoyo.parse import parse_raw_irc_command @@ -207,23 +208,35 @@ class IRCClient: def get_ssl_context(self): """Returns an SSL context for connecting over SSL/TLS. - Loads the certifi certs instead of the system-provided certificates if - the system certificate store is empty.""" - context = ssl.create_default_context() - # Check if store is empty - empty_cert_store = list(context.cert_store_stats().values()).count(0) == 3 - if empty_cert_store and "certifi" in sys.modules: - # Can't validate certificates if store is empty, - # load root certificates from certifi instead. - context = ssl.create_default_context(cafile=certifi.where()) + Loads the certifi root certificate bundle if the certifi module is less + than a year old or if the system certificate store is empty. + + The cert store on Windows also seems to have issues, so it's better + to use the certifi provided bundle assuming it's a recent version. + + On MacOS the system cert store is usually empty, as Python does not use + the system provided ones, instead relying on a bundle installed with the + python installer.""" + default_context = ssl.create_default_context() + if "certifi" not in sys.modules: + return default_context + + # Get age of certifi module + certifi_date = datetime.datetime.strptime(certifi.__version__, "%Y.%m.%d") + current_date = datetime.datetime.now() + certifi_age = current_date - certifi_date + + empty_cert_store = ( + list(default_context.cert_store_stats().values()).count(0) == 3 + ) + # 31557600 seconds is approximately 1 year + if empty_cert_store or certifi_age.total_seconds() <= 31557600: PchumLog.info( "Using SSL/TLS context with certifi-provided root certificates." ) - else: - PchumLog.info( - "Using SSL/TLS context with system-provided root certificates." - ) - return context + return ssl.create_default_context(cafile=certifi.where()) + PchumLog.info("Using SSL/TLS context with system-provided root certificates.") + return default_context def connect(self, verify_hostname=True): """initiates the connection to the server set in self.host:self.port