#!/usr/bin/env python3 import os import sys import argparse from subprocess import Popen, PIPE, getoutput from apt.cache import Cache from getpass import getpass from gpg import Context from pycurl import Curl import certifi _PACKAGES_LIST = { "wget": False, "gnupg2": False, "gnupg-agent": False, "dirmngr": False, "cryptsetup": False, "scdaemon": False, "pcscd": False, "secure-delete": False, "hopenpgp-tools": False, "yubikey-personalization": False } _SERVICES_TO_SHUTDOWN = { "network-manager", "NetworkManager", "avahi-daemon" } # ===== START OF CORE FUNCTIONS ===== def out_success(text): print("[+] " + text) def out_error(text): print("[!] ERROR: " + text) def out_info(text): print("[i] " + text) def out_question_yes_no(text): print(text) resp = input("Please choose (Y/y/N/n): ") if resp.lower() == "y": return 1 else: return 0 def out_question_arbitrary(text): return input(text) # ===== END OF CORE FUNCTIONS ===== # ===== START OF FUNCTIONS ===== def verify_yk(): return out_question_yes_no("Did you verify your YubiKey?") def verify_live(): live = os.system(_CMD_WHOAMI + " | grep \"user\"") # Rewrite that. Detect live system not username # Maybe detect the device root is mounted on? /dev/sdx if live is 0: return True return False def kill_network(): cmdchain = "" for service in _SERVICES_TO_SHUTDOWN: cmdchain += "sudo "+_SUDO_ARGS+_CMD_SERVICE+" "+service+" stop;" for interface in os.listdir("/sys/class/net"): if not interface == "lo": cmdchain += "sudo "+_SUDO_ARGS+_CMD_IFCONFIG+" "+interface+" down;" proc = Popen(cmdchain, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, text=True) if not _USE_ASKPASS: passwd = getpass(); proc.communicate(passwd + "\n") proc.wait() def check_dependencies(): cache = Cache() ret = True for package in _PACKAGES_LIST: if cache[package].is_installed: _PACKAGES_LIST[package] = True else: ret = False return ret def install_dependencies(): cache = Cache() if _SUDO_V19: pass # Do sudo v1.9 stuff here #cache.update() #cache.open() # for package in packages_list: # if packages_list[package] is False: # cache[package].mark_install() # cache.commit() # Remember to drop sudo privs here else: missing_packages = "" for package in _PACKAGES_LIST: if _PACKAGES_LIST[package] is False: missing_packages += package+" " proc = Popen( "sudo "+_SUDO_ARGS+"apt update;" "sudo "+_SUDO_ARGS+"apt install -y "+missing_packages, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, text=True) if not _USE_ASKPASS: passwd = getpass(); proc.communicate(passwd+"\n") proc.wait() passwd = None def download_conf(): with open(_WORKDIR+"/gpg.conf", 'wb') as f: c = Curl() c.setopt(c.URL, "https://raw.githubusercontent.com/drduh/config/master/gpg.conf") c.setopt(c.WRITEDATA, f) c.setopt(c.CAINFO, certifi.where()) c.perform() c.close() out_success("Configuration downloaded!") # ===== START KEY GENERATION ===== def keygen(): c = Context(True, home_dir=_WORKDIR) uid = out_question_arbitrary("Your name: ")+" <"+out_question_arbitrary("Your E-Mail: ")+">" passwd = getoutput("gpg --gen-random --armor 0 24") print("This is your passphrase. Write it down somewhere safe!\n"+passwd) # Create master key masterkey = c.create_key(uid, "rsa4096", expires=False, certify=True, passphrase=passwd) master_fpr = masterkey.fpr seckey = c.get_key(master_fpr, True) # create subkeys sign_key = c.create_subkey(seckey, "rsa4096", 31536000, sign=True) encrypt_key = c.create_subkey(seckey, "rsa4096", 31536000, encrypt=True) auth_key = c.create_subkey(seckey, "rsa4096", 31536000, authenticate=True) # Export master key with open(_WORKDIR+"/master.sec.asc", "wb") as f: f.write(c.key_export_secret(master_fpr)) f.flush() f.close() with open(_WORKDIR+"/sub.sec.asc", "wb") as f: f.write(c.key_export_secret(sign_key.fpr)) f.write(c.key_export_secret(encrypt_key.fpr)) f.write(c.key_export_secret(auth_key.fpr)) f.flush() f.close() out_success("Keys generated!") # ===== END KEY GENERATION ===== # ===== END OF FUNCTIONS ===== # ===== START OF MAIN ===== parser = argparse.ArgumentParser( description="This script tries to accomplish what @drdruh describes in the README\n" "WARNING: This script only works on Debian and Ubuntu" ) parser.add_argument("--verified-yk", action="store_true", help="Skip the YubiKey verification check (!DANGEROUS! If your YK is compromised your keys are too!)") parser.add_argument("--skip-live", action="store_true", help="Skip the live system check (!DANGEROUS! Data could be saved on your hard disk)") parser.add_argument("-d1", "--sec-backup-device", nargs=1, type=str, help="Device to send the secret keys backup to") parser.add_argument("-d2", "--public-backup-device", nargs=1, type=str, help="Device to send the public key backup to") parser.add_argument("--create-backup-usb", nargs=1, type=str, help="Path to device to create a encrypted backup") parser.add_argument("--no-hardened", action="store_false", help="Don't use the hardened configuration (WARNING: you could generate weak keys!)") args = parser.parse_args() # ===== CONSTANTS ===== _YK_VERIFIED = args.verified_yk _LIVE_VERIFIED = args.skip_live _USE_HARDENED_CONF = args.no_hardened _SUDO_V19 = False _SUDO_ARGS = "-S" _USE_ASKPASS = False _CMD_SERVICE = getoutput("which service") _CMD_IFCONFIG = getoutput("which ifconfig") _CMD_WHOAMI = getoutput("which whoami") _BACKUP_SEC_DEVICE = args.sec_backup_device _BACKUP_PUB_DEVICE = args.public_backup_device _BACKUP_DEVICE = args.create_backup_usb _WORKDIR = getoutput("mktemp -d") print(_WORKDIR) # ===== CONSTANTS ===== # ===== PRE-CHECKS ===== try: import sudo _SUDO_V19 = True # Not used yet tho. Don't know the sudo plugin API. except ModuleNotFoundError: pass if os.environ.get("SUDO_ASKPASS") is not None: _USE_ASKPASS = True _SUDO_ARGS += ("A " if _USE_ASKPASS else " ") if _BACKUP_SEC_DEVICE is not None and _BACKUP_PUB_DEVICE is not None: if _BACKUP_DEVICE is not None: out_error("Please use either \"--sec-backup-device\" with \"--public-backup-device\" or \"--create-backup-usb\"") exit(1) else: if _BACKUP_DEVICE is None: out_error("Please use either \"--sec-backup-device\" with \"--public-backup-device\" or \"--create-backup-usb\"") exit(1) # ===== END PRE-CHECKS ===== if not _YK_VERIFIED and verify_yk() is not 1: os.system("/bin/bash -c \"x-www-browser 'https://www.yubico.com/genuine/'\"") out_error("Please verify before proceeding!") exit(1) if _LIVE_VERIFIED or verify_live(): out_success("Great! You seem to be on a live system!") else: out_error("You are not on a live system! Please boot into one or pass the \"--skip-live\" flag!") exit(1) if check_dependencies(): out_success("Great! All packages are installed!") else: if out_question_yes_no("Some packages are missing. Would you like me to install them?"): out_info("Installing packages...") install_dependencies() out_success("Everything set up!") else: out_error("Sorry can't run without them.") exit(1) if _USE_HARDENED_CONF: download_conf() out_info("Shutting down all interfaces...") kill_network() out_success("Seems like we are ready to go. YAY! Let's generate keys!") # TODO:Setup backup device here keygen() # ===== END MAIN =====