From 3734ae7b04e25afaab48ed799cc7129928486dca Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 21 May 2020 16:33:26 -0700 Subject: [PATCH] Propose interfaces --- certbot/certbot/interfaces.py | 36 +++++++++++++++++++++++++++++++++++ certbot/certbot/ocsp.py | 26 +++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/certbot/certbot/interfaces.py b/certbot/certbot/interfaces.py index 81b41d1ec..729c38806 100644 --- a/certbot/certbot/interfaces.py +++ b/certbot/certbot/interfaces.py @@ -1,5 +1,6 @@ """Certbot client interfaces.""" import abc +import enum import six import zope.interface @@ -589,6 +590,41 @@ class RenewableCert(object): """ + +@six.add_metaclass(abc.ABCMeta): +class OCSPResponse(object): + """I'll have a real description someday.""" + + @abc.abstractproperty + def certificate_status(self): + """Cert status + + :rtype: OCSPCertStatus + + """ + + @abc.abstractproperty + def next_update(self): + """Next update + + :rtype: stdlib datetime thing + + """ + + @abc.abstractpoperty + def raw_bytes(self): + """Raw bytes of the OCSP Response + + :rtype: bytes + + """ + + +class OCSPCertStatus(enum): + GOOD = 1 + REVOKED = 2 + + # Updater interfaces # # When "certbot renew" is run, Certbot will iterate over each lineage and check diff --git a/certbot/certbot/ocsp.py b/certbot/certbot/ocsp.py index 51ada012a..0dbeab863 100644 --- a/certbot/certbot/ocsp.py +++ b/certbot/certbot/ocsp.py @@ -20,6 +20,7 @@ from acme.magic_typing import Optional from acme.magic_typing import Tuple from certbot import crypto_util from certbot import errors +from certbot import interfaces from certbot import util from certbot.compat.os import getenv from certbot.interfaces import RenewableCert # pylint: disable=unused-import @@ -29,19 +30,23 @@ try: # and signature_hash_algorithm attribute in OCSPResponse class from cryptography.x509 import ocsp # pylint: disable=ungrouped-imports getattr(ocsp.OCSPResponse, 'signature_hash_algorithm') + CRYPTOGRAPHY_OCSP_AVAILABLE = True except (ImportError, AttributeError): # pragma: no cover - ocsp = None # type: ignore + CRYPTOGRAPHY_OCSP_AVAILABLE = False logger = logging.getLogger(__name__) +class _CryptographyOCSPResponse(interfaces.OCSPResponse): + """Cryptography implementation of given interface.""" + class RevocationChecker(object): """This class figures out OCSP checking on this system, and performs it.""" def __init__(self, enforce_openssl_binary_usage=False): self.broken = False - self.use_openssl_binary = enforce_openssl_binary_usage or not ocsp + self.use_openssl_binary = enforce_openssl_binary_usage or not CRYPTOGRAPHY_OCSP_AVAILABLE if self.use_openssl_binary: if not util.exe_exists("openssl"): @@ -58,6 +63,23 @@ class RevocationChecker(object): else: self.host_args = lambda host: ["Host", host] + def ocsp_response_by_paths(self, cert_path, chain_path, timeout=10): + """Obtains a validated OCSP response. + + The OCSP response could have any cert status, however, if an + OCSP response is returned from this function, the caller knows + it is properly timestamped, signed, etc. + + :param str cert_path: Certificate filepath + :param str chain_path: Certificate chain + :param int timeout: Timeout (in seconds) for the OCSP query + + :returns: The OCSP response if it could be obtained and + validated, otherwise, None + :rtype: interfaces.OCSPResponse or None + + """ + def ocsp_revoked(self, cert): # type: (RenewableCert) -> bool """Get revoked status for a particular cert version.