@@ -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;
}
@@ -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;