From b44be9a5b8c69313c410a29e5c16094fbcf5f9d6 Mon Sep 17 00:00:00 2001 From: tacticalDevC Date: Sun, 16 Feb 2020 17:08:14 +0100 Subject: [PATCH] Adding the main objective. It's still kind of an beta but it seems to work... Signed-off-by: tacticalDevC --- scripts/keygen.py | 283 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 scripts/keygen.py diff --git a/scripts/keygen.py b/scripts/keygen.py new file mode 100644 index 0000000..1a0a48c --- /dev/null +++ b/scripts/keygen.py @@ -0,0 +1,283 @@ +#!/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 ===== \ No newline at end of file