From 06a52879a85b71e40881493ea465dd6d2acd1544 Mon Sep 17 00:00:00 2001 From: nervuri Date: Wed, 9 Feb 2022 17:53:39 +0000 Subject: [PATCH] Add certificate validation capability. --- gemini-demo.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/gemini-demo.py b/gemini-demo.py index 6168c85..dbc1272 100755 --- a/gemini-demo.py +++ b/gemini-demo.py @@ -8,6 +8,12 @@ import ssl import tempfile import textwrap import urllib.parse +import socks + +timeout = 5 + +tor_validation = True +accepted_certs = {} caps = mailcap.getcaps() menu = [] @@ -47,11 +53,72 @@ while True: # Do the Gemini transaction try: while True: - s = socket.create_connection((parsed_url.netloc, 1965)) context = ssl.SSLContext() context.check_hostname = False context.verify_mode = ssl.CERT_NONE - s = context.wrap_socket(s, server_hostname = parsed_url.netloc) + + host = parsed_url.hostname + port = parsed_url.port or 1965 + + s = socket.create_connection((host, port), timeout) + s = context.wrap_socket(s, server_hostname = host) + cert = s.getpeercert(True) + + if tor_validation \ + and (host not in accepted_certs \ + or accepted_certs[host] != cert): + + # Verify TLS certificate from the vantage point + # of a random Tor exit node. + print("Validating certificate...") + + # Get certificate via Tor (SOCKS5 @ localhost:9050). + # DNS lookup is done over Tor. + tor_socket = socks.socksocket() + tor_socket.set_proxy(socks.SOCKS5, "127.0.0.1", 9050, rdns=True) + tor_socket.settimeout(timeout) + tor_connection_successful = True + validation_problem_encountered = False + try: + tor_socket.connect((host, port)) + except socks.ProxyConnectionError: + validation_problem_encountered = True + tor_connection_successful = False + print("Tor proxy not available on localhost:9050.") + print("Certificate validation can't be performed.") + print("Continue browsing with validation disabled? [y/N]") + if input(">> ").strip().lower() == "y": + tor_validation = False + else: + raise Exception("Connection cancelled.") + except socks.GeneralProxyError: + validation_problem_encountered = True + tor_connection_successful = False + print("Tor connection timed out.") + print("Certificate validation can't be performed.") + print("Continue connection? [y/N]") + if input(">> ").strip().lower() != "y": + raise Exception("Connection cancelled.") + if tor_connection_successful: + tor_socket = context.wrap_socket(tor_socket, server_hostname = host) + cert_via_tor = tor_socket.getpeercert(True) + tor_socket.shutdown(socket.SHUT_RDWR) + tor_socket.close() + # Compare certs. + if cert != cert_via_tor: + validation_problem_encountered = True + print("[SECURITY WARNING] Certificate validation failed!") + print("This MIGHT be a Man-in-the-Middle attack.") + print("Continue connection? [y/N]") + if input(">> ").strip().lower() != "y": + raise Exception("Connection cancelled.") + + accepted_certs[host] = cert + + if not validation_problem_encountered: + print("OK") + print() + s.sendall((url + '\r\n').encode("UTF-8")) # Get header and check for redirects fp = s.makefile("rb")