diff --git a/CMakeLists.txt b/CMakeLists.txt index 807862c..90940cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,7 @@ add_executable(${PROJECT_NAME} src/utils.cpp src/utils.hpp src/widgets.cpp src/widgets.hpp src/follow_process_log.hpp src/follow_process_log.cpp + src/crypto.cpp src/crypto.hpp src/tui.cpp src/tui.hpp src/main.cpp ) diff --git a/meson.build b/meson.build index 90a02cf..d920ad9 100644 --- a/meson.build +++ b/meson.build @@ -49,6 +49,7 @@ src_files = files( 'src/utils.cpp', 'src/utils.hpp', 'src/widgets.cpp', 'src/widgets.hpp', 'src/follow_process_log.cpp', 'src/follow_process_log.hpp', + 'src/crypto.cpp', 'src/crypto.hpp', 'src/tui.cpp', 'src/tui.hpp', 'src/main.cpp', ) diff --git a/src/crypto.cpp b/src/crypto.cpp new file mode 100644 index 0000000..8012d22 --- /dev/null +++ b/src/crypto.cpp @@ -0,0 +1,240 @@ +#include "crypto.hpp" +#include "config.hpp" +#include "utils.hpp" +#include "widgets.hpp" + +/* clang-format off */ +#include // for Renderer, Button +#include // for ButtonOption +#include // for Component, ScreenI... +#include // for operator|, size +/* clang-format on */ + +using namespace ftxui; + +#ifdef NDEVENV +#include "follow_process_log.hpp" +#endif + +namespace tui { + +static constexpr auto luks_menu_body = "Devices and volumes encrypted using dm_crypt cannot be accessed or\neven seen without being unlocked via a key or password."; +static constexpr auto luks_menu_body2 = "A seperate boot partition without encryption or logical volume management\n(LVM - unless using BIOS Grub) is required."; +static constexpr auto luks_menu_body3 = "The Automatic option uses default encryption settings,\nand is recommended for beginners.\nOtherwise, it is possible to specify cypher and key size parameters manually."; + +bool select_crypt_partition(const std::string_view& text) noexcept { + auto* config_instance = Config::instance(); + auto& config_data = config_instance->data(); + const auto& partitions = std::get>(config_data["PARTITIONS"]); + + auto screen = ScreenInteractive::Fullscreen(); + std::int32_t selected{}; + bool success{}; + auto ok_callback = [&] { + const auto& src = partitions[static_cast(selected)]; + const auto& lines = utils::make_multiline(src, false, " "); + config_data["PARTITION"] = lines[0]; + success = true; + screen.ExitLoopClosure()(); + }; + const auto& content = fmt::format("\n{}\n", text); + detail::menu_widget(partitions, ok_callback, &selected, &screen, content, {.text_size = size(HEIGHT, GREATER_THAN, 1)}); + + return success; +} + +bool get_cryptname(std::string& cryptname) noexcept { + std::string value{"cryptroot"}; + static constexpr auto luks_cryptname_body = "\nSpecify a name for the encrypted block device.\n \nIt is not necessary to prefix it with /dev/mapper/.\nAn example has been provided.\n"; + if (!detail::inputbox_widget(value, luks_cryptname_body, size(HEIGHT, GREATER_THAN, 4))) { + return false; + } + + cryptname = value; + return true; +} + +bool get_crypt_password(std::string& password) noexcept { + std::string value{}; + static constexpr auto luks_pass_body = "\nEnter a password to un/encrypt the partition.\n \nThis should not be the same as\nthe Root account or user account passwords.\n"; + if (!detail::inputbox_widget(value, luks_pass_body, size(HEIGHT, GREATER_THAN, 4))) { + return false; + } + + password = value; + return true; +} + +bool luks_open() noexcept { + auto* config_instance = Config::instance(); + auto& config_data = config_instance->data(); + + config_data["LUKS_ROOT_NAME"] = ""; + config_data["INCLUDE_PART"] = "part\\|crypt\\|lvm"; + utils::umount_partitions(); + utils::find_partitions(); + + // Filter out partitions that don't contain crypt device + const auto& ignore_part = utils::list_non_crypt(); + + /* const auto& parts = utils::make_multiline(ignore_part); + for (const auto& part : parts) { + utils::delete_partition_in_list(part); + }*/ + + // stop if no encrypted partition found + const auto& partitions = std::get>(config_data["PARTITIONS"]); + if (partitions.empty()) { + detail::msgbox_widget("\nNo LUKS-encrypted partition found.\n"); + return false; + } + + // Select encrypted partition to open + /* clang-format off */ + if (!tui::select_crypt_partition(luks_menu_body)) { return false; } + /* clang-format on */ + + // Enter name of the Luks partition and get password to open it + const auto& partition = std::get(config_data["PARTITION"]); + auto& luks_root_name = std::get(config_data["LUKS_ROOT_NAME"]); + auto& luks_password = std::get(config_data["PASSWD"]); + /* clang-format off */ + if (!tui::get_cryptname(luks_root_name)) { return false; } + if (!tui::get_crypt_password(luks_password)) { return false; } + /* clang-format on */ + + spdlog::info("partition: {}, luks_root_name: {}, luks_password: {}", partition, luks_root_name, luks_password); + + // Try to open the luks partition with the credentials given. If successful show this, otherwise + // show the error + detail::infobox_widget("\nPlease wait...\n"); +#ifdef NDEVENV + detail::follow_process_log_widget({"/bin/sh", "-c", fmt::format("echo \"{}\" | cryptsetup open --type luks {} {}", luks_password, partition, luks_root_name)}); +#endif + + const auto& devlist = utils::exec(fmt::format("lsblk -o NAME,TYPE,FSTYPE,SIZE,MOUNTPOINT {} | grep \"crypt\\|NAME\\|MODEL\\|TYPE\\|FSTYPE\\|SIZE\"", partition)); + detail::msgbox_widget(devlist); + + return true; +} + +bool luks_setup() noexcept { + auto* config_instance = Config::instance(); + auto& config_data = config_instance->data(); + +#ifdef NDEVENV + utils::exec("modprobe -a dm-mod dm_crypt"); +#endif + config_data["INCLUDE_PART"] = "part\\|lvm"; + utils::umount_partitions(); + utils::find_partitions(); + + // Select partition to encrypt + /* clang-format off */ + static constexpr auto luks_encrypt_body = "Select a partition to encrypt."; + if (!tui::select_crypt_partition(luks_encrypt_body)) { return false; } + /* clang-format on */ + + // Enter name of the Luks partition and get password to create it + auto& luks_root_name = std::get(config_data["LUKS_ROOT_NAME"]); + auto& luks_password = std::get(config_data["PASSWD"]); + /* clang-format off */ + if (!tui::get_cryptname(luks_root_name)) { return false; } + if (!tui::get_crypt_password(luks_password)) { return false; } + /* clang-format on */ + + return true; +} + +void luks_encrypt([[maybe_unused]] const std::string_view& command) noexcept { + // Encrypt selected partition or LV with credentials given + detail::infobox_widget("\nPlease wait...\n"); + std::this_thread::sleep_for(std::chrono::seconds(2)); +#ifdef NDEVENV + auto* config_instance = Config::instance(); + auto& config_data = config_instance->data(); + const auto& partition = std::get(config_data["PARTITION"]); + const auto& luks_root_name = std::get(config_data["LUKS_ROOT_NAME"]); + const auto& luks_password = std::get(config_data["PASSWD"]); + + detail::follow_process_log_widget({"/bin/sh", "-c", fmt::format("echo \"{}\" | cryptsetup -q {} {}", luks_password, command, partition)}); + + // Now open the encrypted partition or LV + detail::follow_process_log_widget({"/bin/sh", "-c", fmt::format("echo \"{}\" | cryptsetup open {} {}", luks_password, partition, luks_root_name)}); +#endif +} + +void luks_default() noexcept { + tui::luks_encrypt("--type luks1 luksFormat"); +} + +bool luks_key_define() noexcept { + std::string value{"-s 512 -c aes-xts-plain64"}; + static constexpr auto luks_cipher_key = "\nOnce the specified flags have been amended,\nthey will automatically be used with the 'cryptsetup -q luksFormat /dev/...' command.\n \nNOTE: Key files are not supported;\nthey can be added manually post-installation.\nDo not specify any additional flags such as -v (--verbose) or -y (--verify-passphrase)."; + if (!detail::inputbox_widget(value, luks_cipher_key, size(HEIGHT, GREATER_THAN, 4))) { + return false; + } + + tui::luks_encrypt(value); + return true; +} + +void luks_express() noexcept { + tui::luks_encrypt("--pbkdf-force-iterations 200000 --type luks1 luksFormat"); +} + +void luks_show() noexcept { + static constexpr auto luks_success = "Done! Opened and ready for LVM (recommended) or direct mounting."; + const auto& lsblk = utils::exec("lsblk -o NAME,TYPE,FSTYPE,SIZE | grep \"part\\|crypt\\|NAME\\|TYPE\\|FSTYPE\\|SIZE\""); + const auto& content = fmt::format("\n{}\n \n{}", luks_success, lsblk); + detail::msgbox_widget(content, size(HEIGHT, GREATER_THAN, 5)); +} + +void luks_menu_advanced() noexcept { + const std::vector menu_entries = { + "Open Encrypted Partition", + "Automatic LUKS Encryption", + "Define Key-Size and Cypher", + "Express LUKS", + "Back", + }; + + auto screen = ScreenInteractive::Fullscreen(); + bool success{}; + std::int32_t selected{}; + auto ok_callback = [&] { + success = true; + screen.ExitLoopClosure()(); + }; + + const auto& content = fmt::format("\n{}\n \n{}\n \n{}\n", luks_menu_body, luks_menu_body2, luks_menu_body3); + detail::menu_widget(menu_entries, ok_callback, &selected, &screen, content); + /* clang-format off */ + if (!success) { return; } + + switch (selected) { + case 0: + tui::luks_open(); + break; + case 1: { + if (!tui::luks_setup()) { return; } + tui::luks_default(); + tui::luks_show(); + break; + } + case 2: { + if (!tui::luks_setup() && !tui::luks_key_define()) { return; } + tui::luks_show(); + break; + } + case 3: { + if (!tui::luks_setup()) { return; } + tui::luks_express(); + tui::luks_show(); + break; + } + } + /* clang-format on */ +} + +} // namespace tui diff --git a/src/crypto.hpp b/src/crypto.hpp new file mode 100644 index 0000000..043f866 --- /dev/null +++ b/src/crypto.hpp @@ -0,0 +1,8 @@ +#ifndef CRYPTO_HPP +#define CRYPTO_HPP + +namespace tui { +void luks_menu_advanced() noexcept; +} // namespace tui + +#endif // CRYPTO_HPP diff --git a/src/tui.cpp b/src/tui.cpp index d812385..30719b9 100644 --- a/src/tui.cpp +++ b/src/tui.cpp @@ -1,5 +1,6 @@ #include "tui.hpp" #include "config.hpp" +#include "crypto.hpp" #include "definitions.hpp" #include "utils.hpp" #include "widgets.hpp" @@ -726,7 +727,7 @@ void install_base() noexcept { auto* config_instance = Config::instance(); auto& config_data = config_instance->data(); const auto& mountpoint = std::get(config_data["MOUNTPOINT"]); - const std::vector available_kernels{"linux-cachyos", "linux", "linux-zen", "linux-lts", "linux-cachyos-cacule", "linux-cachyos-cacule-rdb", "linux-cachyos-bmq", "linux-cachyos-pds", "linux-cachyos-baby", "linux-cachyos-cacule-lts"}; + const std::vector available_kernels{"linux-cachyos", "linux", "linux-zen", "linux-lts", "linux-cachyos-cacule", "linux-cachyos-bmq", "linux-cachyos-pds", "linux-cachyos-tt", "linux-cachyos-bore"}; // Create the base list of packages std::vector install_packages{}; @@ -771,7 +772,7 @@ void install_base() noexcept { const auto& pkg = pkg_list[i]; pkg_list.emplace_back(fmt::format("{}-headers", pkg)); } - pkg_list.insert(pkg_list.cend(), {"base", "base-devel", "cachyos-keyring", "cachyos-mirrorlist", "cachyos-v3-mirrorlist"}); + pkg_list.insert(pkg_list.cend(), {"base", "base-devel", "cachyos-keyring", "cachyos-mirrorlist", "cachyos-v3-mirrorlist", "cachyos-hello", "cachyos-hooks", "cachyos-settings", "cachyos-rate-mirrors", "cachy-browser"}); packages = utils::make_multiline(pkg_list, false, " "); spdlog::info(fmt::format("Preparing for pkgs to install: \"{}\"", packages)); @@ -848,7 +849,7 @@ void install_desktop() noexcept { /* clang-format off */ static constexpr std::array to_be_inserted{"plasma-desktop", "plasma-framework", "plasma-nm", "plasma-pa", "plasma-workspace", "konsole", "kate", "dolphin", "sddm", "sddm-kcm", "plasma", "plasma-wayland-protocols", "plasma-wayland-session", - "gamemode", "lib32-gamemode", "ksysguard", "pamac-aur", "openssh", "htop"}; + "gamemode", "lib32-gamemode", "ksysguard", "pamac-aur", "openssh", "btop"}; /* clang-format on */ pkg_list.insert(pkg_list.end(), std::move_iterator(to_be_inserted.begin()), std::move_iterator(to_be_inserted.end())); @@ -1997,7 +1998,6 @@ void prep_menu() noexcept { case 1: tui::show_devices(); break; - case 2: { utils::umount_partitions(); if (tui::select_device()) { @@ -2005,12 +2005,14 @@ void prep_menu() noexcept { } break; } + case 5: + tui::luks_menu_advanced(); + break; case 7: tui::mount_partitions(); break; case 3: case 4: - case 5: case 6: case 8: case 9: diff --git a/src/widgets.cpp b/src/widgets.cpp index 8dd25d9..652e4af 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -189,7 +189,7 @@ void infobox_widget(const std::string_view& content, Decorator boxsize) noexcept Dimension::Full() // Height ); - auto element = centered_widget_nocontrols("New CLI Installer", multiline_text(utils::make_multiline(content.data())) | vcenter | boxsize); + auto element = centered_widget_nocontrols("New CLI Installer", multiline_text(utils::make_multiline(content)) | vcenter | boxsize); Render(screen, element); screen.Print(); } @@ -214,7 +214,7 @@ bool yesno_widget(const std::string_view& content, Decorator boxsize) noexcept { }); auto renderer = Renderer(container, [&] { - return centered_widget(container, "New CLI Installer", multiline_text(utils::make_multiline(content.data())) | hcenter | boxsize); + return centered_widget(container, "New CLI Installer", multiline_text(utils::make_multiline(content)) | hcenter | boxsize); }); screen.Loop(renderer);