- In the Web 3.0 Data Space, identity is linked to reality. We begin
- by verifying your real-world passport, which serves as the
- foundation for issuing your secure ePassport. At the same time, we
- generate your eName – a unique digital identifier – and create your
- eVault to store and protect your personal data.
-
-
- Next
-
+ {#if hardwareAvailable === false}
+
+ Hardware Security Not Available
+
+
+ Your device doesn't support hardware-backed security keys required for passport verification.
+
+
+ Please use the pre-verification code option to create a demo account instead.
+
+ In the Web 3.0 Data Space, identity is linked to reality. We begin
+ by verifying your real-world passport, which serves as the
+ foundation for issuing your secure ePassport. At the same time, we
+ generate your eName – a unique digital identifier – and create your
+ eVault to store and protect your personal data.
+
+
+ Next
+
+ {/if}
{/if}
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte
index 55938bb83..f8da33724 100644
--- a/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte
@@ -227,6 +227,13 @@ onMount(async () => {
// Check hardware key support first
await checkHardwareKeySupport();
+ // If hardware is not available, redirect back to onboarding
+ if (!hardwareKeySupported) {
+ console.log("Hardware not available, redirecting to onboarding");
+ await goto("/onboarding");
+ return;
+ }
+
// Initialize key manager and check if default key pair exists
await initializeKeyManager();
await ensureKeyForVerification();
From f80bf7a88923db8cc91cf9ff29824cfb235d8103 Mon Sep 17 00:00:00 2001
From: Merul Dhiman
Date: Thu, 13 Nov 2025 22:55:13 +0530
Subject: [PATCH 5/5] feat: final safety checks for hardware keys
---
.../gen/android/.idea/deviceManager.xml | 13 ++
.../src/lib/crypto/KeyManagerFactory.ts | 9 +-
.../src/routes/(auth)/onboarding/+page.svelte | 139 ++++++++++++------
.../src/routes/(auth)/verify/+page.svelte | 52 ++++++-
4 files changed, 156 insertions(+), 57 deletions(-)
create mode 100644 infrastructure/eid-wallet/src-tauri/gen/android/.idea/deviceManager.xml
diff --git a/infrastructure/eid-wallet/src-tauri/gen/android/.idea/deviceManager.xml b/infrastructure/eid-wallet/src-tauri/gen/android/.idea/deviceManager.xml
new file mode 100644
index 000000000..91f95584d
--- /dev/null
+++ b/infrastructure/eid-wallet/src-tauri/gen/android/.idea/deviceManager.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/infrastructure/eid-wallet/src/lib/crypto/KeyManagerFactory.ts b/infrastructure/eid-wallet/src/lib/crypto/KeyManagerFactory.ts
index 5e793adbb..1ccdfbd61 100644
--- a/infrastructure/eid-wallet/src/lib/crypto/KeyManagerFactory.ts
+++ b/infrastructure/eid-wallet/src/lib/crypto/KeyManagerFactory.ts
@@ -85,10 +85,11 @@ export class KeyManagerFactory {
isFake: boolean,
): Promise {
// Pre-verification users (isFake=true) or pre-verification context should NEVER use hardware
- const shouldUseHardware = !isFake &&
- context !== "pre-verification" &&
- (await KeyManagerFactory.isHardwareAvailable());
-
+ const shouldUseHardware =
+ !isFake &&
+ context !== "pre-verification" &&
+ (await KeyManagerFactory.isHardwareAvailable());
+
const config: KeyManagerConfig = {
keyId,
useHardware: shouldUseHardware,
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte
index dc1e61990..cbbb7e2ab 100644
--- a/infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte
@@ -23,44 +23,57 @@ let verificationId = $state("");
let demoName = $state("");
let verificationSuccess = $state(false);
let keyManager: KeyManager | null = $state(null);
-let hardwareAvailable = $state(null);
+let showHardwareError = $state(false);
let checkingHardware = $state(false);
const KEY_ID = "default";
const handleGetStarted = async () => {
+ isPaneOpen = true;
+ preVerified = false;
checkingHardware = true;
+ showHardwareError = false;
+ error = null;
+
try {
if (!globalState) {
globalState = getContext<() => GlobalState>("globalState")();
}
- const isAvailable = await globalState.keyService.isHardwareAvailable();
- hardwareAvailable = isAvailable;
-
- if (!isAvailable) {
- // Hardware not available - show error and don't proceed
- error = "Your device doesn't support hardware-backed security keys required for passport verification. Please use the pre-verification code option instead.";
- isPaneOpen = true;
- preVerified = false;
- setTimeout(() => {
- error = null;
- }, 8000);
+
+ // Actually try to generate a test hardware key
+ const testKeyId = `hardware-test-${Date.now()}`;
+ console.log(
+ "Testing hardware key generation with test key:",
+ testKeyId,
+ );
+
+ try {
+ const { manager, created } = await globalState.keyService.ensureKey(
+ testKeyId,
+ "onboarding",
+ );
+ console.log(
+ "Test key result - Manager type:",
+ manager.getType(),
+ "Created:",
+ created,
+ );
+
+ // Check if we got hardware manager and it actually created a key
+ if (manager.getType() !== "hardware") {
+ throw new Error("Got software fallback instead of hardware");
+ }
+
+ // Hardware works! Clean up test key and proceed
+ console.log("Hardware keys are working");
+ checkingHardware = false;
+ } catch (keyError) {
+ console.error("Hardware key test failed:", keyError);
+ showHardwareError = true;
checkingHardware = false;
- return;
}
-
- // Hardware is available - proceed to verification
- isPaneOpen = true;
- preVerified = false;
} catch (err) {
- console.error("Failed to check hardware availability:", err);
- hardwareAvailable = false;
- error = "Unable to check device capabilities. Please use the pre-verification code option instead.";
- isPaneOpen = true;
- preVerified = false;
- setTimeout(() => {
- error = null;
- }, 8000);
- } finally {
+ console.error("Error checking hardware:", err);
+ showHardwareError = true;
checkingHardware = false;
}
};
@@ -138,8 +151,24 @@ async function getApplicationPublicKey() {
}
const handleNext = async () => {
- //handle next functionlity
- goto("/verify");
+ // Initialize keys for onboarding context before going to verify
+ try {
+ loading = true;
+ if (!globalState) {
+ globalState = getContext<() => GlobalState>("globalState")();
+ }
+ await initializeKeyManager();
+ await ensureKeyForContext();
+ loading = false;
+ goto("/verify");
+ } catch (err) {
+ console.error("Failed to initialize keys for onboarding:", err);
+ error = "Failed to initialize security keys. Please try again.";
+ loading = false;
+ setTimeout(() => {
+ error = null;
+ }, 5000);
+ }
};
let globalState: GlobalState;
@@ -342,12 +371,21 @@ onMount(async () => {
{/if}
{:else}
- {#if hardwareAvailable === false}
+ {#if checkingHardware}
+
+
+
+
Checking device capabilities...
+
+
+ {:else if showHardwareError}
Hardware Security Not Available
- Your device doesn't support hardware-backed security keys required for passport verification.
+ Your phone doesn't support hardware crypto keys, which is a requirement for verified IDs.
Please use the pre-verification code option to create a demo account instead.
@@ -364,21 +402,32 @@ onMount(async () => {
{:else}
-
- Your Digital Self begins with the Real You
-
-
- In the Web 3.0 Data Space, identity is linked to reality. We begin
- by verifying your real-world passport, which serves as the
- foundation for issuing your secure ePassport. At the same time, we
- generate your eName – a unique digital identifier – and create your
- eVault to store and protect your personal data.
-
-
- Next
-
+ {#if loading}
+
+
+
+
Initializing security keys...
+
+
+ {:else}
+
+ Your Digital Self begins with the Real You
+
+
+ In the Web 3.0 Data Space, identity is linked to reality. We begin
+ by verifying your real-world passport, which serves as the
+ foundation for issuing your secure ePassport. At the same time, we
+ generate your eName – a unique digital identifier – and create your
+ eVault to store and protect your personal data.
+
+
+ Next
+
+ {/if}
{/if}
{/if}
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte
index f8da33724..d2f042f26 100644
--- a/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte
@@ -109,12 +109,41 @@ let hardwareKeyCheckComplete = $state(false);
const KEY_ID = "default";
async function handleVerification() {
- const { data } = await axios.post(
- new URL("/verification", PUBLIC_PROVISIONER_URL).toString(),
- );
- verificaitonId.set(data.id);
- showVeriffModal = true;
- watchEventStream(data.id);
+ try {
+ // Ensure keys are initialized before starting verification
+ if (!keyManager) {
+ try {
+ await initializeKeyManager();
+ await ensureKeyForVerification();
+ } catch (keyError) {
+ console.error("Failed to initialize keys:", keyError);
+ // If key initialization fails, go back to onboarding
+ await goto("/onboarding");
+ return;
+ }
+ }
+
+ const { data } = await axios.post(
+ new URL("/verification", PUBLIC_PROVISIONER_URL).toString(),
+ );
+ verificaitonId.set(data.id);
+ showVeriffModal = true;
+ watchEventStream(data.id);
+ } catch (error) {
+ console.error("Failed to start verification:", error);
+ // If verification fails due to key issues or any initialization error, go back to onboarding
+ const errorMessage =
+ error instanceof Error
+ ? error.message.toLowerCase()
+ : String(error).toLowerCase();
+ if (
+ errorMessage.includes("key") ||
+ errorMessage.includes("initialize") ||
+ errorMessage.includes("manager")
+ ) {
+ await goto("/onboarding");
+ }
+ }
}
function watchEventStream(id: string) {
@@ -235,8 +264,15 @@ onMount(async () => {
}
// Initialize key manager and check if default key pair exists
- await initializeKeyManager();
- await ensureKeyForVerification();
+ try {
+ await initializeKeyManager();
+ await ensureKeyForVerification();
+ } catch (error) {
+ console.error("Failed to initialize keys for verification:", error);
+ // If key initialization fails, redirect back to onboarding
+ await goto("/onboarding");
+ return;
+ }
handleContinue = async () => {
if ($status !== "approved" && $status !== "duplicate")