From a4df0271ff795ded76153804533248e778451a35 Mon Sep 17 00:00:00 2001 From: Saptak S Date: Sun, 23 Apr 2023 19:40:02 +0530 Subject: [PATCH] Adds tor browser signature verification using GPG and signature files --- desktop/scripts/get-tor.py | 119 +++++++++--------- .../scripts/kounek7zrdx745qydx6p59t9mqjpuhdf | Bin 0 -> 6694 bytes 2 files changed, 57 insertions(+), 62 deletions(-) create mode 100644 desktop/scripts/kounek7zrdx745qydx6p59t9mqjpuhdf diff --git a/desktop/scripts/get-tor.py b/desktop/scripts/get-tor.py index a8d1b94f..30a86ed1 100644 --- a/desktop/scripts/get-tor.py +++ b/desktop/scripts/get-tor.py @@ -8,11 +8,12 @@ import shutil import subprocess import requests import click +import tempfile +import johnnycanencrypt as jce -torbrowser_version_url = ( +torbrowser_latest_url = ( "https://aus1.torproject.org/torbrowser/update_3/release/downloads.json" ) -torbrowser_root_url = "https://dist.torproject.org/torbrowser" # Common paths root_path = os.path.dirname( @@ -21,43 +22,25 @@ root_path = os.path.dirname( working_path = os.path.join(root_path, "build", "tor") -def get_expected_platform_sha256(platform_filename, torbrowser_version): - r = requests.get( - f"{torbrowser_root_url}/{torbrowser_version}/sha256sums-signed-build.txt" - ) - for checksum_item in r.content.decode().split("\n"): - [checksum, filename] = checksum_item.split() - if filename == platform_filename: - return checksum - - return None - - def get_latest_tor_version_urls(platform): - r = requests.get(torbrowser_version_url) + r = requests.get(torbrowser_latest_url) if r.status_code != 200 or platform not in r.json()["downloads"]: - print("Tor browser version url not working") + print("Tor browser latest version url not working") sys.exit(-1) - torbrowser_version = r.json()["version"] platform_url = r.json()["downloads"][platform]["ALL"]["binary"] + platform_sig_url = r.json()["downloads"][platform]["ALL"]["sig"] platform_filename = platform_url.split("/")[-1] - expected_platform_sha256 = get_expected_platform_sha256( - platform_filename, torbrowser_version - ) - if not expected_platform_sha256: - print(f"Expected sha256sum for {platform} not found") - sys.exit(-1) - - return platform_url, platform_filename, expected_platform_sha256 + return platform_url, platform_filename, platform_sig_url -def get_tor_windows(win_url, win_filename, expected_win_sha256): +def get_tor_windows(ks, torkey, win_url, win_filename, expected_win_sig): bin_filenames = ["tor.exe"] # Build paths win_path = os.path.join(working_path, win_filename) + win_sig_path = os.path.join(working_path, f"{win_filename}.asc") dist_path = os.path.join(root_path, "onionshare", "resources", "tor") # Make sure the working folder exists @@ -69,19 +52,20 @@ def get_tor_windows(win_url, win_filename, expected_win_sha256): print("Downloading {}".format(win_url)) r = requests.get(win_url) open(win_path, "wb").write(r.content) - win_sha256 = hashlib.sha256(r.content).hexdigest() - else: - print("Already downloaded: {}".format(win_path)) - win_data = open(win_path, "rb").read() - win_sha256 = hashlib.sha256(win_data).hexdigest() - # Compare the hash - if win_sha256 != expected_win_sha256: - print("ERROR! The sha256 doesn't match:") - print("expected: {}".format(expected_win_sha256)) - print(" actual: {}".format(win_sha256)) + # Make sure Tor Browser signature is downloaded + if not os.path.exists(win_sig_path): + print("Downloading {}".format(expected_win_sig)) + r = requests.get(expected_win_sig) + open(win_sig_path, "wb").write(r.content) + + # Verify the signature + if not ks.verify_file_detached(torkey, win_path, win_sig_path): + print("ERROR! The .exe file verification with the signature failed!") sys.exit(-1) + print("Tor Browser verification successful!") + # Extract the bits we need from the exe subprocess.Popen( [ @@ -123,12 +107,13 @@ def get_tor_windows(win_url, win_filename, expected_win_sha256): update_tor_bridges() -def get_tor_macos(macos_url, macos_filename, expected_macos_sha256): +def get_tor_macos(ks, torkey, macos_url, macos_filename, expected_macos_sig): # Build paths dmg_tor_path = os.path.join( "/Volumes", "Tor Browser", "Tor Browser.app", "Contents" ) dmg_path = os.path.join(working_path, macos_filename) + dmg_sig_path = os.path.join(working_path, f"{macos_filename}.asc") dist_path = os.path.join(root_path, "onionshare", "resources", "tor") if not os.path.exists(dist_path): os.makedirs(dist_path, exist_ok=True) @@ -142,18 +127,20 @@ def get_tor_macos(macos_url, macos_filename, expected_macos_sha256): print("Downloading {}".format(macos_url)) r = requests.get(macos_url) open(dmg_path, "wb").write(r.content) - dmg_sha256 = hashlib.sha256(r.content).hexdigest() - else: - dmg_data = open(dmg_path, "rb").read() - dmg_sha256 = hashlib.sha256(dmg_data).hexdigest() - # Compare the hash - if dmg_sha256 != expected_macos_sha256: - print("ERROR! The sha256 doesn't match:") - print("expected: {}".format(expected_macos_sha256)) - print(" actual: {}".format(dmg_sha256)) + # Make sure the signature is downloaded + if not os.path.exists(dmg_sig_path): + print("Downloading {}".format(expected_macos_sig)) + r = requests.get(expected_macos_sig) + open(dmg_sig_path, "wb").write(r.content) + + # Verify the signature + if not ks.verify_file_detached(torkey, dmg_path, dmg_sig_path): + print("ERROR! The dmg file verification with the signature failed!") sys.exit(-1) + print("Tor Browser verification successful!") + # Mount the dmg, copy data to the working path subprocess.call(["hdiutil", "attach", dmg_path]) @@ -183,9 +170,10 @@ def get_tor_macos(macos_url, macos_filename, expected_macos_sha256): update_tor_bridges() -def get_tor_linux64(linux64_url, linux64_filename, expected_linux64_sha256): +def get_tor_linux64(ks, torkey, linux64_url, linux64_filename, expected_linux64_sig): # Build paths tarball_path = os.path.join(working_path, linux64_filename) + tarball_sig_path = os.path.join(working_path, f"{linux64_filename}.asc") dist_path = os.path.join(root_path, "onionshare", "resources", "tor") # Make sure dirs exist @@ -200,18 +188,20 @@ def get_tor_linux64(linux64_url, linux64_filename, expected_linux64_sha256): print("Downloading {}".format(linux64_url)) r = requests.get(linux64_url) open(tarball_path, "wb").write(r.content) - tarball_sha256 = hashlib.sha256(r.content).hexdigest() - else: - tarball_data = open(tarball_path, "rb").read() - tarball_sha256 = hashlib.sha256(tarball_data).hexdigest() - # Compare the hash - if tarball_sha256 != expected_linux64_sha256: - print("ERROR! The sha256 doesn't match:") - print("expected: {}".format(expected_linux64_sha256)) - print(" actual: {}".format(tarball_sha256)) + # Make sure the signature file is downloaded + if not os.path.exists(tarball_sig_path): + print("Downloading {}".format(expected_linux64_sig)) + r = requests.get(expected_linux64_sig) + open(tarball_sig_path, "wb").write(r.content) + + # Verify signature + if not ks.verify_file_detached(torkey, tarball_path, tarball_sig_path): + print("ERROR! The tarball verification with the signature failed!") sys.exit(-1) + print("Tor Browser verification successful!") + # Delete extracted tarball, if it's there shutil.rmtree(os.path.join(working_path, "tor-browser"), ignore_errors=True) @@ -318,24 +308,29 @@ def main(platform): click.echo(f"platform must be one of: {valid_platforms}") return - global platform_url, platform_filename, expected_platform_sha256 ( platform_url, platform_filename, - expected_platform_sha256, + expected_platform_sig, ) = get_latest_tor_version_urls(platform) + tmpdir = tempfile.TemporaryDirectory() + ks = jce.KeyStore(tmpdir.name) + torkey = ks.import_key(os.path.join(root_path, "scripts", "kounek7zrdx745qydx6p59t9mqjpuhdf")) + print(f"Tor GPG key: {torkey}") if platform == "win32": - get_tor_windows(platform_url, platform_filename, expected_platform_sha256) + get_tor_windows(ks, torkey, platform_url, platform_filename, expected_platform_sig) elif platform == "win64": - get_tor_windows(platform_url, platform_filename, expected_platform_sha256) + get_tor_windows(ks, torkey, platform_url, platform_filename, expected_platform_sig) elif platform == "macos": - get_tor_macos(platform_url, platform_filename, expected_platform_sha256) + get_tor_macos(ks, torkey, platform_url, platform_filename, expected_platform_sig) elif platform == "linux64": - get_tor_linux64(platform_url, platform_filename, expected_platform_sha256) + get_tor_linux64(ks, torkey, platform_url, platform_filename, expected_platform_sig) else: click.echo("invalid platform") + tmpdir.cleanup() + if __name__ == "__main__": main() diff --git a/desktop/scripts/kounek7zrdx745qydx6p59t9mqjpuhdf b/desktop/scripts/kounek7zrdx745qydx6p59t9mqjpuhdf new file mode 100644 index 0000000000000000000000000000000000000000..8f4e9750ffcb176731e61dbb1b0fc96546759a24 GIT binary patch literal 6694 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 GH~$CUAF~($ literal 0 HcmV?d00001