From: Simon Glass <simon.glass@canonical.com> The unlock process is quite different with a TKey, since we need to load the signing app with the user's passphrase as the USS. If the TKey is not already in firmware mode we must prompt the user to remove and re-insert it. The easiest way to implement this is via a state machine. Add the required logic: - detect key presence and absence - prompt for removal and insertion - send the app, while keeping the UI responsive - unlock the disk once the TKey provides a key - report success or failure to the user Ensure that the logic is only used if CONFIG_TKEY and CONFIG_LUKS are both enabled. Co-developed-by: Claude <claude@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- boot/bootctl/logic.c | 670 +++++++++++++++++++++++++++++++++++++++- include/bootctl/logic.h | 52 ++++ 2 files changed, 721 insertions(+), 1 deletion(-) diff --git a/boot/bootctl/logic.c b/boot/bootctl/logic.c index be00c965b66..96cc270ae2c 100644 --- a/boot/bootctl/logic.c +++ b/boot/bootctl/logic.c @@ -10,8 +10,12 @@ #include <bootctl.h> #include <dm.h> +#include <hexdump.h> #include <log.h> +#include <luks.h> +#include <part.h> #include <time.h> +#include <tkey.h> #include <version.h> #include <bootctl/logic.h> #include <bootctl/measure.h> @@ -20,9 +24,12 @@ #include <bootctl/ui.h> #include <bootctl/util.h> #include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <u-boot/sha256.h> enum { - COUNTDOWN_INTERVAL_MS = 1000, /* inteval between autoboot updates */ + COUNTDOWN_INTERVAL_MS = 1000, /* interval between autoboot updates */ + ERROR_TIMEOUT_MS = 5000, /* timeout for error message display */ }; static int logic_prepare(struct udevice *dev) @@ -64,6 +71,17 @@ static int logic_prepare(struct udevice *dev) return log_msg_ret("blo", ret); } + /* Find TKey device if enabled (test can override this) */ + if (priv->opt_tkey) { + ret = uclass_find_first_device(UCLASS_TKEY, &priv->tkey); + if (ret || !priv->tkey) { + log_debug("TKey not found at startup\n"); + } else { + log_debug("TKey '%s'\n", priv->tkey->name); + /* Device found but not probed yet - not present */ + priv->tkey_present = false; + } + } return 0; } @@ -181,6 +199,615 @@ static int read_images(struct udevice *dev, struct osinfo *osinfo) return 0; } +/** + * show_unlock_error() - Display error message and update UI state + * + * Helper function to show an error message, display error state, + * and hide the pass prompt. + * + * @priv: Logic private data + * @seq: Sequence number of the selected OS + * @msg: Error message to display + * Return: 0 if OK, -ve on error + */ +static int show_unlock_error(struct logic_priv *priv, int seq, const char *msg) +{ + int ret; + + ret = bc_ui_set_pass_msg(priv->ui, seq, msg); + if (ret && ret != -ENOSYS) + return log_msg_ret("sem", ret); + ret = bc_ui_show_pass_msg(priv->ui, seq, true); + if (ret && ret != -ENOSYS) + return log_msg_ret("see", ret); + ret = bc_ui_show_pass(priv->ui, seq, false); + if (ret) + return log_msg_ret("hsp", ret); + + return 0; +} + +/** + * start_tkey_load() - Start TKey app loading + * + * Starts the app loading process. Expects TKey device to be already probed + * and present. + * + * @dev: Logic device + * @pass: User passphrase to use as USS + * Return: 0 on success or if needs replug (state set appropriately), + * -ve on error + */ +static int start_tkey_load(struct udevice *dev, const char *pass) +{ + struct logic_priv *priv = dev_get_priv(dev); + int ret; + + ret = tkey_load_start(&priv->tkey_load_ctx, priv->tkey, + (const u8 *)__signer_1_0_0_begin, + TKEY_SIGNER_SIZE, (const u8 *)pass, strlen(pass)); + if (ret) + return ret; + log_debug("Started TKey app loading (%zx bytes)\n", TKEY_SIGNER_SIZE); + + return 0; +} + +/** + * derive_tkey_disk_key() - Derive disk encryption key from TKey public key + * + * Gets the public key from the TKey and derives the disk encryption key + * using SHA256. Must match the Python tkey-fde-key.py implementation. + * + * @dev: Logic device + * Return: 0 on success, -ve on error + */ +static int derive_tkey_disk_key(struct udevice *dev) +{ + struct logic_priv *priv = dev_get_priv(dev); + u8 pubkey[TKEY_PUBKEY_SIZE]; + char pubkey_hex[TKEY_PUBKEY_SIZE * 2 + 1]; + sha256_context ctx; + int ret; + + /* Get public key from the loaded app */ + ret = tkey_get_pubkey(priv->tkey, pubkey); + if (ret) { + log_warning("Failed to get TKey public key (err=%dE)\n", ret); + return ret; + } + + /* + * Derive disk encryption key from public key using SHA256 + * Must match Python tkey-fde-key.py implementation which does: + * hashlib.sha256(pubkey.encode()).digest() + * + * This converts the binary public key to hex string, + * then hashes the string bytes. + */ + bin2hex(pubkey_hex, pubkey, TKEY_PUBKEY_SIZE); + + sha256_starts(&ctx); + sha256_update(&ctx, (const u8 *)pubkey_hex, TKEY_PUBKEY_SIZE * 2); + sha256_finish(&ctx, priv->tkey_disk_key); + + log_info("TKey disk key derived successfully\n"); + + return 0; +} + +/** + * perform_tkey_unlock() - Perform TKey-based LUKS unlock + * + * This function performs LUKS unlock using the TKey-derived key as binary + * passphrase material. Expects TKey to be already loaded and key derived. + * + * @dev: Logic device + * @os: OS information containing the encrypted bootflow + * @seq: Sequence number of the selected OS + * @master_key: Buffer to store the unlocked master key + * @key_size: Pointer to key size (input: buffer size, output: actual size) + * Return: 0 if unlock succeeded, -ENOENT if there is no encrypted partition, + * other -ve on other error + */ +static int perform_tkey_unlock(struct udevice *dev, struct osinfo *os, int seq, + u8 *master_key, u32 *key_sizep) +{ + struct logic_priv *priv = dev_get_priv(dev); + struct disk_partition pinfo; + int ret; + + /* TKey key should already be derived at this point */ + assert(priv->ustate == UNS_TKEY_UNLOCK); + + /* Get partition info for the encrypted partition (next after boot) */ + ret = part_get_info(dev_get_uclass_plat(os->bflow.blk), + os->bflow.part + 1, &pinfo); + if (ret) { + log_warning("Failed to get partition info (err=%dE)\n", ret); + return -ENOENT; + } + + /* + * Use TKey-derived key as binary passphrase input to LUKS KDF + * The key is treated as binary passphrase material that will be + * processed by PBKDF2/Argon2 just like a text passphrase would be. + * This matches how cryptsetup --key-file works. + */ + log_info("Using LUKS1 unlock with binary passphrase\n"); + ret = luks_unlock(os->bflow.blk, &pinfo, priv->tkey_disk_key, + TKEY_DISK_KEY_SIZE, true, master_key, key_sizep); + if (ret) + return log_msg_ret("htu", ret); + + return 0; +} + +/** + * handle_idle() - Set up the passphrase prompt UI + * + * This handles the UNS_IDLE state, showing the passphrase prompt and + * transitioning to UNS_WAITING_PASS. + * + * @dev: Logic device + * @seq: Sequence number of the selected OS + * Return: 0 on success, -ve on error + */ +static int handle_idle(struct udevice *dev, int seq) +{ + struct logic_priv *priv = dev_get_priv(dev); + int ret; + + /* show passphrase prompt and hide any error */ + ret = bc_ui_show_pass_msg(priv->ui, seq, false); + if (ret && ret != -ENOSYS) + return log_msg_ret("hse", ret); + ret = bc_ui_show_pass(priv->ui, seq, true); + if (ret) + return log_msg_ret("lsp", ret); + priv->ustate = UNS_WAITING_PASS; + priv->selected_seq = seq; + priv->refresh = true; + + return 0; +} + +/** + * handle_waiting_pass() - Handle waiting for passphrase entry + * + * This handles the UNS_WAITING_PASS state, getting the passphrase and + * showing the "Unlocking..." message, then transitioning to either + * UNS_UNLOCK_NORMAL or UNS_TKEY_START based on configuration. + * + * @dev: Logic device + * @seq: Sequence number of the selected OS + * Return: 0 on success, -ve on error + */ +static int handle_waiting_pass(struct udevice *dev, int seq) +{ + struct logic_priv *priv = dev_get_priv(dev); + const char *pass; + int ret; + + /* Get pass and show "Unlocking..." message */ + ret = bc_ui_get_pass(priv->ui, seq, &pass); + if (ret) { + log_warning("Failed to get pass (err=%dE)\n", ret); + priv->ustate = UNS_IDLE; + return -EAGAIN; /* Return to menu */ + } + + /* Show "Unlocking..." message */ + ret = bc_ui_set_pass_msg(priv->ui, seq, "Unlocking..."); + if (ret && ret != -ENOSYS) + return log_msg_ret("spu", ret); + ret = bc_ui_show_pass_msg(priv->ui, seq, true); + if (ret && ret != -ENOSYS) + return log_msg_ret("ssu", ret); + ret = bc_ui_show_pass(priv->ui, seq, false); + if (ret) + return log_msg_ret("hsp", ret); + + /* Select unlock path based on TKey option */ + if (priv->opt_tkey) + priv->ustate = UNS_TKEY_START; + else + priv->ustate = UNS_UNLOCK_NORMAL; + priv->refresh = true; + + return 0; +} + +/** + * handle_unlock_normal() - Perform normal LUKS unlock with direct passphrase + * + * This handles the UNS_UNLOCK_NORMAL state, performing LUKS unlock + * with direct passphrase. + * + * @dev: Logic device + * @os: OS information + * @seq: Sequence number of the selected OS + * Return: 0 on normal operation, -ve on error + */ +static int handle_unlock_normal(struct udevice *dev, struct osinfo *os, int seq) +{ + struct logic_priv *priv = dev_get_priv(dev); + struct disk_partition pinfo; + u8 master_key[128]; + u32 key_size = sizeof(master_key); + const char *pass; + int ret; + + /* Get pass for direct LUKS unlock */ + ret = bc_ui_get_pass(priv->ui, seq, &pass); + if (ret) { + log_warning("Failed to get pass (err=%dE)\n", ret); + priv->ustate = UNS_IDLE; + return 0; + } + + /* Get partition info for the encrypted partition (next after boot) */ + ret = part_get_info(dev_get_uclass_plat(os->bflow.blk), + os->bflow.part + 1, &pinfo); + if (ret) { + log_warning("Failed to get partition info (err=%dE)\n", ret); + priv->ustate = UNS_IDLE; + return 0; + } + + /* Try to unlock with the pass */ + ret = luks_unlock(os->bflow.blk, &pinfo, (const u8 *)pass, strlen(pass), + false, master_key, &key_size); + + /* Store result and transition to result handling state */ + priv->unlock_result = ret; + priv->ustate = UNS_UNLOCK_RESULT; + priv->refresh = true; + + return 0; +} + +/** + * handle_tkey_wait_remove() - Handle TKey removal wait state + * + * This handles the UNS_TKEY_WAIT_REMOVE state, waiting for TKey to be + * physically removed and then transitioning to UNS_TKEY_WAIT_INSERT. + * + * @dev: Logic device + * @seq: Sequence number of the selected OS + * Return: 0 on normal operation, -ve on error + */ +static int handle_tkey_wait_remove(struct udevice *dev, int seq) +{ + struct logic_priv *priv = dev_get_priv(dev); + int ret; + + /* Check if TKey is still present by checking app mode */ + ret = tkey_in_app_mode(priv->tkey); + if (ret < 0) { + /* TKey removed (error accessing device) */ + log_debug("TKey removed, cleaning up device\n"); + device_remove(priv->tkey, DM_REMOVE_NORMAL); + priv->tkey_present = false; + log_debug("TKey removed, ready for next attempt\n"); + + /* Show replug message */ + ret = show_unlock_error(priv, seq, "Please insert TKey"); + if (ret) + return log_msg_ret("rpe", ret); + priv->refresh = true; + priv->ustate = UNS_TKEY_WAIT_INSERT; + + return 0; + } + + return 0; +} + +/** + * handle_tkey_start() - Handle TKey unlock start + * + * Checks if TKey device is available and transitions to wait for insert state. + * + * @dev: Logic device + * @seq: Sequence number of the selected OS + * Return: 0 on normal operation, -ve on error + */ +static int handle_tkey_start(struct udevice *dev, int seq) +{ + struct logic_priv *priv = dev_get_priv(dev); + + /* Check if TKey device is available */ + if (!priv->tkey) { + log_err("TKey device not found\n"); + show_unlock_error(priv, seq, "TKey not found"); + return -ENODEV; + } + priv->ustate = UNS_TKEY_WAIT_INSERT; + + return 0; +} + +/** + * handle_tkey_wait_insert() - Handle TKey insertion wait state + * + * This handles the UNS_TKEY_WAIT_INSERT state, probing for TKey device + * and transitioning to UNS_TKEY_INSERTED when found. + * + * @dev: Logic device + * @seq: Sequence number of the selected OS + * Return: -EAGAIN to continue waiting or transition to next state + */ +static int handle_tkey_wait_insert(struct udevice *dev, int seq) +{ + struct logic_priv *priv = dev_get_priv(dev); + int ret; + + log_debug("Probing TKey device\n"); + ret = device_probe(priv->tkey); + if (ret) { + /* Probe failed - device not yet inserted */ + log_debug("TKey not inserted yet, waiting\n"); + priv->ustate = UNS_TKEY_WAIT_INSERT; + return 0; + } + /* Probe succeeded - device is present */ + log_debug("TKey probed successfully\n"); + priv->tkey_present = true; + priv->ustate = UNS_TKEY_INSERTED; + + return 0; +} + +/** + * handle_tkey_inserted() - Handle TKey inserted state + * + * This handles the UNS_TKEY_INSERTED state, starting the TKey app loading + * process with the user's passphrase. + * + * @dev: Logic device + * @seq: Sequence number of the selected OS + * Return: 0 on normal operation, -ve on error + */ +static int handle_tkey_inserted(struct udevice *dev, int seq) +{ + struct logic_priv *priv = dev_get_priv(dev); + const char *pass; + int ret; + + /* Get passphrase for TKey derivation */ + ret = bc_ui_get_pass(priv->ui, seq, &pass); + if (ret) { + log_warning("Failed to get pass (err=%dE)\n", ret); + priv->ustate = UNS_IDLE; + return 0; /* Return to menu */ + } + + /* Start loading TKey app with USS */ + ret = start_tkey_load(dev, pass); + if (ret == -ENOTSUPP) { + /* TKey in app mode, needs to be replugged */ + log_debug("TKey not in firmware mode, needs replug\n"); + priv->ustate = UNS_TKEY_WAIT_REMOVE; + + /* Show replug message */ + ret = show_unlock_error(priv, seq, "Please remove TKey"); + if (ret) + return log_msg_ret("rpe", ret); + priv->refresh = true; + return 0; + } else if (ret) { + log_warning("Failed to start TKey app load (err=%dE)\n", ret); + return ret; + } + + priv->ustate = UNS_TKEY_LOADING; + + return 0; +} + +/** + * handle_tkey_loading() - Handle TKey app loading state + * + * This handles the UNS_TKEY_LOADING state, sending blocks of the TKey app + * and transitioning to UNS_TKEY_READY when complete. + * + * @dev: Logic device + * @seq: Sequence number of the selected OS + * Return: 0 on normal operation, -ve on error + */ +static int handle_tkey_loading(struct udevice *dev, int seq) +{ + struct logic_priv *priv = dev_get_priv(dev); + int ret; + + /* Send 1 block per poll to keep the UI responsive */ + ret = tkey_load_next(&priv->tkey_load_ctx, 1); + if (ret == -EAGAIN) { + char msg[64]; + int percent; + + /* More blocks to send */ + log_debug("TKey loading: %u/%u bytes\n", + priv->tkey_load_ctx.offset, + priv->tkey_load_ctx.app_size); + + /* Show loading progress - round up so 100% will show */ + percent = ((priv->tkey_load_ctx.offset + 1) * 100 + + priv->tkey_load_ctx.app_size - 1) / + priv->tkey_load_ctx.app_size; + if (percent > 100) + percent = 100; + snprintf(msg, sizeof(msg), "Preparing TKey... %d%%", percent); + bc_ui_set_pass_msg(priv->ui, seq, msg); + priv->refresh = true; + return 0; + } + + if (ret) { + log_warning("Failed to load TKey app (err=%dE)\n", ret); + priv->ustate = UNS_TKEY_START; + return ret; + } + + /* Loading complete, now derive disk key */ + log_info("TKey app loaded successfully, deriving disk key\n"); + priv->ustate = UNS_TKEY_READY; + + return 0; +} + +/** + * handle_tkey_ready() - Handle TKey ready state + * + * This handles the UNS_TKEY_READY state, deriving the disk encryption key + * from the TKey public key and transitioning to IN_PROGRESS state. + * + * @dev: Logic device + * @seq: Sequence number of the selected OS + * Return: 0 on normal operation, -ve on error + */ +static int handle_tkey_ready(struct udevice *dev, int seq) +{ + struct logic_priv *priv = dev_get_priv(dev); + int ret; + + /* Derive disk encryption key from TKey public key */ + ret = derive_tkey_disk_key(dev); + if (ret) + return ret; + bc_ui_set_pass_msg(priv->ui, seq, "Unlocking..."); + priv->refresh = true; + + /* Key derived, start unlock */ + priv->ustate = UNS_TKEY_UNLOCK; + + return 0; +} + +/** + * handle_tkey_unlock() - Handle TKey-based LUKS unlock state + * + * This handles the UNS_TKEY_UNLOCK state, performing LUKS unlock with + * TKey-derived key and transitioning to UNS_UNLOCK_RESULT. + * + * @dev: Logic device + * @os: OS information + * @seq: Sequence number of the selected OS + * Return: 0 on normal operation, -ve on error + */ +static int handle_tkey_unlock(struct udevice *dev, struct osinfo *os, int seq) +{ + struct logic_priv *priv = dev_get_priv(dev); + u8 master_key[128]; + u32 key_size = sizeof(master_key); + int ret; + + ret = perform_tkey_unlock(dev, os, seq, master_key, &key_size); + + /* Store result and transition to result handling state */ + priv->unlock_result = ret; + priv->ustate = UNS_UNLOCK_RESULT; + priv->refresh = true; + + return 0; +} + +/** + * handle_unlock_result() - Handle the result of unlock operation + * + * Processes unlock result, showing either error or success message. + * On error, shows "Incorrect passphrase" and transitions to UNS_BAD_PASS. + * On success, shows "Unlock successful" and transitions to UNS_OK. + * + * @priv: Logic private data + * @seq: Sequence number of the selected OS + * @unlock_ret: Return value from unlock operation + * Return: -EAGAIN always (to wait for next poll) + */ +static int handle_unlock_result(struct logic_priv *priv, int seq, + int unlock_ret) +{ + int ret; + + if (unlock_ret) { + log_warning("Failed to unlock LUKS partition (err=%dE)\n", + unlock_ret); + + /* Set and show error message, hide pass prompt */ + ret = show_unlock_error(priv, seq, "Incorrect passphrase"); + if (ret) + return log_msg_ret("ipe", ret); + /* Display error for 5 seconds before allowing retry */ + priv->time_error = get_timer(0); + priv->ustate = UNS_BAD_PASS; + priv->refresh = true; + return 0; + } + + log_info("LUKS partition unlocked successfully\n"); + /* Set and show success message briefly, hide pass prompt */ + ret = show_unlock_error(priv, seq, "Unlock successful"); + if (ret) + return log_msg_ret("suc", ret); + /* Show success message for one poll cycle, then boot */ + priv->ustate = UNS_OK; + priv->refresh = true; + /* TODO: Create blkmap device for decrypted access */ + + return 0; +} + +static int handle_encrypted_tkey(struct udevice *dev, struct osinfo *os, + int seq) +{ + struct logic_priv *priv = dev_get_priv(dev); + + if (!IS_ENABLED(CONFIG_TKEY)) + return -ENOSYS; + + switch (priv->ustate) { + case UNS_TKEY_WAIT_REMOVE: + return handle_tkey_wait_remove(dev, seq); + case UNS_TKEY_START: + return handle_tkey_start(dev, seq); + case UNS_TKEY_WAIT_INSERT: + return handle_tkey_wait_insert(dev, seq); + case UNS_TKEY_INSERTED: + return handle_tkey_inserted(dev, seq); + case UNS_TKEY_LOADING: + return handle_tkey_loading(dev, seq); + case UNS_TKEY_READY: + return handle_tkey_ready(dev, seq); + case UNS_TKEY_UNLOCK: + return handle_tkey_unlock(dev, os, seq); + default: + return -EINVAL; + } +} + +static int handle_encrypted(struct udevice *dev, struct osinfo *os, int seq) +{ + struct logic_priv *priv = dev_get_priv(dev); + + switch (priv->ustate) { + case UNS_IDLE: + return handle_idle(dev, seq); + case UNS_WAITING_PASS: + return handle_waiting_pass(dev, seq); + case UNS_UNLOCK_NORMAL: + return handle_unlock_normal(dev, os, seq); + case UNS_BAD_PASS: + case UNS_OK: + /* These states shouldn't reach here in normal flow */ + return -EINVAL; + case UNS_UNLOCK_RESULT: + return handle_unlock_result(priv, seq, priv->unlock_result); + default: + return handle_encrypted_tkey(dev, os, seq); + } +} + static int logic_poll(struct udevice *dev) { struct logic_priv *priv = dev_get_priv(dev); @@ -207,6 +834,26 @@ static int logic_poll(struct udevice *dev) } } + /* if unlock succeeded - show a message and boot on the next poll */ + if (priv->ustate == UNS_OK) { + /* Success message is set, prepare to boot after rendering */ + priv->ustate = UNS_IDLE; + priv->ready_to_boot = true; + priv->refresh = true; + } + + /* Check if error message display timeout has expired */ + if (priv->ustate == UNS_BAD_PASS && priv->time_error && + get_timer(priv->time_error) > ERROR_TIMEOUT_MS) { + /* Hide error message and allow retry */ + ret = bc_ui_show_pass_msg(priv->ui, priv->selected_seq, false); + if (ret && ret != -ENOSYS) + return log_msg_ret("hse", ret); + priv->time_error = 0; + priv->ustate = UNS_IDLE; + priv->refresh = true; + } + if (priv->autoboot_active && get_timer(priv->start_time) > priv->next_countdown) { ulong secs = get_timer(priv->start_time) / 1000; @@ -230,18 +877,38 @@ static int logic_poll(struct udevice *dev) else if (ret) priv->refresh = true; + /* Ignore menu selection while displaying error message */ + if (selected && priv->ustate == UNS_BAD_PASS) + selected = false; + if (!selected && priv->autoboot_active && !priv->autoboot_remain_s && seq >= 0) { log_info("Selecting %d due to timeout\n", seq); selected = true; } + /* If ready to unlock, trigger selection to continue unlock process */ + if (!selected && priv->ustate != UNS_WAITING_PASS && + priv->ustate != UNS_IDLE && priv->ustate != UNS_BAD_PASS) { + seq = priv->selected_seq; + selected = true; + log_debug("Continuing unlock for seq %d\n", seq); + } + if (selected) { struct osinfo *os; os = alist_getw(&priv->osinfo, seq, struct osinfo); if (!os) return log_msg_ret("gos", -ENOENT); + + /* If encrypted, handle pass entry and unlock */ + if (IS_ENABLED(CONFIG_LUKS) && + (os->bflow.flags & BOOTFLOWF_ENCRYPTED)) { + ret = handle_encrypted(dev, os, seq); + if (ret) + return ret; + } priv->ready_to_boot = false; priv->selected_seq = seq; } @@ -294,6 +961,7 @@ static int logic_of_to_plat(struct udevice *dev) priv->opt_autoboot = ofnode_read_bool(node, "autoboot"); priv->opt_measure = ofnode_read_bool(node, "measure"); priv->opt_slow_refresh = ofnode_read_bool(node, "slow-refresh"); + priv->opt_tkey = ofnode_read_bool(node, "tkey"); return 0; } diff --git a/include/bootctl/logic.h b/include/bootctl/logic.h index a1de68235ac..62a0223d080 100644 --- a/include/bootctl/logic.h +++ b/include/bootctl/logic.h @@ -10,9 +10,43 @@ #define __bootctl_logic_h #include <bootctl/oslist.h> +#include <tkey.h> struct udevice; +/** + * enum unlock_state - State of the disk unlock process + * + * @UNS_IDLE: No unlock in progress + * @UNS_WAITING_PASS: Waiting for user to enter passphrase + * @UNS_UNLOCK_NORMAL: Unlocking with direct passphrase + * @UNS_TKEY_START: Unlocking with TKey + * @UNS_TKEY_WAIT_REMOVE: Waiting for TKey to be removed (after wrong passphrase) + * @UNS_TKEY_WAIT_INSERT: Waiting for TKey to be inserted (after removal) + * @UNS_TKEY_INSERTED: TKey inserted, starting app load + * @UNS_TKEY_LOADING: Loading TKey app + * @UNS_TKEY_READY: TKey key derived, ready to unlock + * @UNS_TKEY_UNLOCK: Unlocking LUKS partition + * @UNS_UNLOCK_RESULT: Processing unlock result (success or failure) + * @UNS_BAD_PASS: Unlock failed, showing error message + * @UNS_OK: Unlock succeeded, showing success message + */ +enum unlock_state { + UNS_IDLE, + UNS_WAITING_PASS, + UNS_UNLOCK_NORMAL, + UNS_TKEY_START, + UNS_TKEY_WAIT_REMOVE, + UNS_TKEY_WAIT_INSERT, + UNS_TKEY_INSERTED, + UNS_TKEY_LOADING, + UNS_TKEY_READY, + UNS_TKEY_UNLOCK, + UNS_UNLOCK_RESULT, + UNS_BAD_PASS, + UNS_OK, +}; + /** * struct logic_priv - Information maintained by the boot logic as it works * @@ -27,6 +61,7 @@ struct udevice; * @opt_autoboot: true to autoboot the default OS after a timeout * @opt_measure: true to measure loaded images, etc. * @opt_slow_refresh: refresh the UI only when needed + * @opt_tkey: true to use TKey for unlocking encrypted volumes * * @state_loaded: true if the state information has been loaded * @scanning: true if scanning for new OSes @@ -40,6 +75,14 @@ struct udevice; * @selected_seq: sequence number of OS waiting for passphrase, or -1 if none * @ready_to_boot: true if success message shown, ready to boot on next poll * + * @tkey: TKey device (pointer never changes once set) + * @tkey_present: true if TKey is physically present and accessible + * @tkey_load_ctx: TKey app loading context for iterative loading + * @tkey_disk_key: Buffer to store derived disk key from TKey + * @ustate: Current state of the disk unlock process + * @unlock_result: Result of disk unlock (0 = OK, -ve on error) + * @time_error: monotonic time when error message display started + * * @iter: oslist iterator, used to find new OSes * @meas: TPM-measurement device * @oslist: provides OSes to boot; we iterate through each osinfo driver to find @@ -57,6 +100,7 @@ struct logic_priv { bool opt_autoboot; bool opt_measure; bool opt_slow_refresh; + bool opt_tkey; bool state_loaded; bool state_saved; @@ -71,6 +115,14 @@ struct logic_priv { int selected_seq; bool ready_to_boot; + struct udevice *tkey; + bool tkey_present; + struct tkey_load_ctx tkey_load_ctx; + u8 tkey_disk_key[TKEY_DISK_KEY_SIZE]; + enum unlock_state ustate; + int unlock_result; + ulong time_error; + struct oslist_iter iter; struct udevice *meas; struct udevice *oslist; -- 2.43.0