commit 82edd42b4f4b292da6923ad4bdc28b443d52d993 Author: canewsin Date: Sun Jan 5 23:58:30 2020 +0530 py3-patches for zeronet mobile diff --git a/CryptConnection.py b/CryptConnection.py new file mode 100644 index 00000000..f3c6b7b6 --- /dev/null +++ b/CryptConnection.py @@ -0,0 +1,225 @@ +import sys +import logging +import os +import ssl +import hashlib +import random + +from Config import config +from util import helper + + +class CryptConnectionManager: + def __init__(self): + this_file = os.path.abspath(__file__).replace("\\", "/").rstrip("cd") + if sys.platform.startswith("win"): + self.openssl_bin = "tools\\openssl\\openssl.exe" + elif config.dist_type.startswith("bundle_linux"): + self.openssl_bin = "../runtime/bin/openssl" + elif "in.canews.zeronet" in this_file: + self.openssl_bin = "../usr/bin/openssl" + else: + self.openssl_bin = "openssl" + + self.context_client = None + self.context_server = None + + self.openssl_conf_template = "src/lib/openssl/openssl.cnf" + self.openssl_conf = config.data_dir + "/openssl.cnf" + + self.openssl_env = { + "OPENSSL_CONF": self.openssl_conf, + "RANDFILE": config.data_dir + "/openssl-rand.tmp" + } + + self.crypt_supported = [] # Supported cryptos + + self.cacert_pem = config.data_dir + "/cacert-rsa.pem" + self.cakey_pem = config.data_dir + "/cakey-rsa.pem" + self.cert_pem = config.data_dir + "/cert-rsa.pem" + self.cert_csr = config.data_dir + "/cert-rsa.csr" + self.key_pem = config.data_dir + "/key-rsa.pem" + + self.log = logging.getLogger("CryptConnectionManager") + self.log.debug("Version: %s" % ssl.OPENSSL_VERSION) + + self.fakedomains = [ + "yahoo.com", "amazon.com", "live.com", "microsoft.com", "mail.ru", "csdn.net", "bing.com", + "amazon.co.jp", "office.com", "imdb.com", "msn.com", "samsung.com", "huawei.com", "ztedevices.com", + "godaddy.com", "w3.org", "gravatar.com", "creativecommons.org", "hatena.ne.jp", + "adobe.com", "opera.com", "apache.org", "rambler.ru", "one.com", "nationalgeographic.com", + "networksolutions.com", "php.net", "python.org", "phoca.cz", "debian.org", "ubuntu.com", + "nazwa.pl", "symantec.com" + ] + + def createSslContexts(self): + if self.context_server and self.context_client: + return False + ciphers = "ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:AES128-SHA256:AES256-SHA:" + ciphers += "!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK" + + if hasattr(ssl, "PROTOCOL_TLS"): + protocol = ssl.PROTOCOL_TLS + else: + protocol = ssl.PROTOCOL_TLSv1_2 + self.context_client = ssl.SSLContext(protocol) + self.context_client.check_hostname = False + self.context_client.verify_mode = ssl.CERT_NONE + + self.context_server = ssl.SSLContext(protocol) + self.context_server.load_cert_chain(self.cert_pem, self.key_pem) + + for ctx in (self.context_client, self.context_server): + ctx.set_ciphers(ciphers) + ctx.options |= ssl.OP_NO_COMPRESSION + try: + ctx.set_alpn_protocols(["h2", "http/1.1"]) + ctx.set_npn_protocols(["h2", "http/1.1"]) + except Exception: + pass + + # Select crypt that supported by both sides + # Return: Name of the crypto + def selectCrypt(self, client_supported): + for crypt in self.crypt_supported: + if crypt in client_supported: + return crypt + return False + + # Wrap socket for crypt + # Return: wrapped socket + def wrapSocket(self, sock, crypt, server=False, cert_pin=None): + if crypt == "tls-rsa": + if server: + sock_wrapped = self.context_server.wrap_socket( + sock, server_side=True) + else: + sock_wrapped = self.context_client.wrap_socket( + sock, server_hostname=random.choice(self.fakedomains)) + if cert_pin: + cert_hash = hashlib.sha256( + sock_wrapped.getpeercert(True)).hexdigest() + if cert_hash != cert_pin: + raise Exception( + "Socket certificate does not match (%s != %s)" % (cert_hash, cert_pin)) + return sock_wrapped + else: + return sock + + def removeCerts(self): + if config.keep_ssl_cert: + return False + for file_name in ["cert-rsa.pem", "key-rsa.pem", "cacert-rsa.pem", "cakey-rsa.pem", "cacert-rsa.srl", "cert-rsa.csr", "openssl-rand.tmp"]: + file_path = "%s/%s" % (config.data_dir, file_name) + if os.path.isfile(file_path): + os.unlink(file_path) + + # Load and create cert files is necessary + def loadCerts(self): + if config.disable_encryption: + return False + + if self.createSslRsaCert() and "tls-rsa" not in self.crypt_supported: + self.crypt_supported.append("tls-rsa") + + # Try to create RSA server cert + sign for connection encryption + # Return: True on success + def createSslRsaCert(self): + casubjects = [ + "/C=US/O=Amazon/OU=Server CA 1B/CN=Amazon", + "/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3", + "/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 High Assurance Server CA", + "/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA" + ] + self.openssl_env['CN'] = random.choice(self.fakedomains) + + if os.path.isfile(self.cert_pem) and os.path.isfile(self.key_pem): + self.createSslContexts() + return True # Files already exits + + import subprocess + + # Replace variables in config template + conf_template = open(self.openssl_conf_template).read() + conf_template = conf_template.replace( + "$ENV::CN", self.openssl_env['CN']) + open(self.openssl_conf, "w").write(conf_template) + + # Generate CAcert and CAkey + cmd_params = helper.shellquote( + self.openssl_bin, + self.openssl_conf, + random.choice(casubjects), + self.cakey_pem, + self.cacert_pem + ) + cmd = "%s req -new -newkey rsa:2048 -days 3650 -nodes -x509 -config %s -subj %s -keyout %s -out %s -batch" % cmd_params + self.log.debug("Generating RSA CAcert and CAkey PEM files...") + self.log.debug("Running: %s" % cmd) + proc = subprocess.Popen( + cmd, shell=True, stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, env=self.openssl_env + ) + back = proc.stdout.read().strip().decode(errors="replace").replace("\r", "") + proc.wait() + + if not (os.path.isfile(self.cacert_pem) and os.path.isfile(self.cakey_pem)): + self.log.error( + "RSA ECC SSL CAcert generation failed, CAcert or CAkey files not exist. (%s)" % back) + return False + else: + self.log.debug("Result: %s" % back) + + # Generate certificate key and signing request + cmd_params = helper.shellquote( + self.openssl_bin, + self.key_pem, + self.cert_csr, + "/CN=" + self.openssl_env['CN'], + self.openssl_conf, + ) + cmd = "%s req -new -newkey rsa:2048 -keyout %s -out %s -subj %s -sha256 -nodes -batch -config %s" % cmd_params + self.log.debug("Generating certificate key and signing request...") + proc = subprocess.Popen( + cmd, shell=True, stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, env=self.openssl_env + ) + back = proc.stdout.read().strip().decode(errors="replace").replace("\r", "") + proc.wait() + self.log.debug("Running: %s\n%s" % (cmd, back)) + + # Sign request and generate certificate + cmd_params = helper.shellquote( + self.openssl_bin, + self.cert_csr, + self.cacert_pem, + self.cakey_pem, + self.cert_pem, + self.openssl_conf + ) + cmd = "%s x509 -req -in %s -CA %s -CAkey %s -set_serial 01 -out %s -days 730 -sha256 -extensions x509_ext -extfile %s" % cmd_params + self.log.debug("Generating RSA cert...") + proc = subprocess.Popen( + cmd, shell=True, stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, env=self.openssl_env + ) + back = proc.stdout.read().strip().decode(errors="replace").replace("\r", "") + proc.wait() + self.log.debug("Running: %s\n%s" % (cmd, back)) + + if os.path.isfile(self.cert_pem) and os.path.isfile(self.key_pem): + self.createSslContexts() + + # Remove no longer necessary files + os.unlink(self.openssl_conf) + os.unlink(self.cacert_pem) + os.unlink(self.cakey_pem) + os.unlink(self.cert_csr) + + return True + else: + self.log.error( + "RSA ECC SSL cert generation failed, cert or key files not exist.") + + +manager = CryptConnectionManager() diff --git a/patches.json b/patches.json new file mode 100644 index 00000000..54e91f51 --- /dev/null +++ b/patches.json @@ -0,0 +1,7 @@ +[ + { + "filename": "CryptConnection.py", + "patchDir": "src/Crypt", + "patchUrl": "https://raw.githubusercontent.com/canewsin/ZeroNet/py3-patches/CryptConnection.py" + } +] \ No newline at end of file