diff --git a/backend/web.py b/backend/web.py index 60b3c3cce11a587cb1a3b0b44158a0e7e161cdca..d1d8626b54ec6436542b29e585496bf093a946c2 100644 --- a/backend/web.py +++ b/backend/web.py @@ -2,17 +2,51 @@ import ssl from rich.console import Console from cryptography import x509 +import socket -import tls_utils as tls_utils +import tls_utils +from tls_utils import TLSDetails -def web_noconn_expiry_days(web_domain: str) -> int | None: - try: - pem_cert = ssl.get_server_certificate((web_domain, 443), timeout=5) - cert = x509.load_pem_x509_certificate(pem_cert.encode()) - except Exception as e: - console = Console() - console.log("Could not grab server cert for", "[orange bold underline]"+web_domain, ":", e, style="orange") - return None - - not_after = cert.not_valid_after.timestamp() - return tls_utils.get_expiry_timestamps(not_after) \ No newline at end of file +class SSLHandler: + def __init__(self, host: str, port: int, context: ssl.SSLContext): + self.host = host + self.port = port + self.context = context + + def connect(self, verification: bool) -> int: + if verification: + with self.context.wrap_socket(socket.socket(), server_hostname=self.host) as s: + s.connect((self.host, self.port)) + cert = s.getpeercert() + return tls_utils.get_validity_days(cert)[1] + else: + pem_cert = ssl.get_server_certificate((self.host, self.port), timeout=5) + cert = x509.load_pem_x509_certificate(pem_cert.encode()) + not_after = cert.not_valid_after.timestamp() + return tls_utils.get_expiry_timestamps(not_after)[1] + +class SSLVerificator: + def __init__(self, context: ssl.SSLContext): + self.context = context + + def connect(self, domain: str, port: int) -> TLSDetails: + handler = SSLHandler(domain, port, self.context) + try: + expiry = handler.connect(True) + return TLSDetails(domain_name=domain, expires_in_days=expiry) + except ssl.SSLCertVerificationError as e: + if e.verify_code == tls_utils.EXPIRED: + expiry = handler.connect(False) + return TLSDetails(domain_name=domain, expires_in_days=expiry) + elif e.verify_code == tls_utils.REVOKED: + return TLSDetails(domain_name=domain, error_message="was revoked.") + elif e.verify_code == tls_utils.SELF_SIGNED: + return TLSDetails(domain_name=domain, error_message="is self-signed.") + elif e.verify_code == tls_utils.ROOT_NOT_TRUSTED: + return TLSDetails(domain_name=domain, error_message="invalid: root not trusted.") + else: + return TLSDetails(domain_name=domain, error_message="failed verification: " + e.verify_message + ".") + except ssl.SSLError as e: + return TLSDetails(domain_name=domain, error_message="could not establish a secure connection: " + e.reason + ".") + except Exception as e: + return TLSDetails(domain_name=domain, error_message="could not connect: " + str(e) + ".") \ No newline at end of file