Skip to content

Commit 5982c51

Browse files
sosweethamcoodos
andauthored
Fix/hardware crypto context logic (#425)
* fix: hardware crypto context logic * fix: hardware crypto context logic * fix: remove useless log * fix: hardware key problem * feat: final safety checks for hardware keys --------- Co-authored-by: Merul Dhiman <[email protected]>
1 parent 73a4e58 commit 5982c51

File tree

5 files changed

+242
-38
lines changed

5 files changed

+242
-38
lines changed

infrastructure/eid-wallet/src-tauri/gen/android/.idea/deviceManager.xml

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

infrastructure/eid-wallet/src/lib/crypto/KeyManagerFactory.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@ export class KeyManagerFactory {
1515
* Get a key manager instance based on the configuration
1616
*/
1717
static async getKeyManager(config: KeyManagerConfig): Promise<KeyManager> {
18-
// If explicitly requesting hardware and not in pre-verification mode
19-
if (config.useHardware && !config.preVerificationMode) {
20-
return KeyManagerFactory.getHardwareKeyManager();
21-
}
22-
23-
// If in pre-verification mode, always use software keys
18+
// If in pre-verification mode, ALWAYS use software keys (never hardware)
2419
if (config.preVerificationMode) {
2520
console.log("Using software key manager for pre-verification mode");
2621
return KeyManagerFactory.getSoftwareKeyManager();
2722
}
2823

24+
// If explicitly requesting hardware and not in pre-verification mode
25+
if (config.useHardware) {
26+
return KeyManagerFactory.getHardwareKeyManager();
27+
}
28+
2929
// Default behavior: try hardware first, fallback to software
3030
try {
3131
const hardwareManager = KeyManagerFactory.getHardwareKeyManager();
@@ -82,11 +82,18 @@ export class KeyManagerFactory {
8282
static async getKeyManagerForContext(
8383
keyId: string,
8484
context: "onboarding" | "signing" | "verification" | "pre-verification",
85+
isFake: boolean,
8586
): Promise<KeyManager> {
87+
// Pre-verification users (isFake=true) or pre-verification context should NEVER use hardware
88+
const shouldUseHardware =
89+
!isFake &&
90+
context !== "pre-verification" &&
91+
(await KeyManagerFactory.isHardwareAvailable());
92+
8693
const config: KeyManagerConfig = {
8794
keyId,
88-
useHardware: context !== "pre-verification",
89-
preVerificationMode: context === "pre-verification",
95+
useHardware: shouldUseHardware,
96+
preVerificationMode: context === "pre-verification" || isFake,
9097
};
9198

9299
return KeyManagerFactory.getKeyManager(config);

infrastructure/eid-wallet/src/lib/global/controllers/key.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { KeyManagerFactory } from "$lib/crypto";
22
import type { KeyManager } from "$lib/crypto";
33
import type { Store } from "@tauri-apps/plugin-store";
4+
import type { UserController } from "./user";
45

56
export type KeyServiceContext =
67
| "onboarding"
@@ -67,20 +68,50 @@ export class KeyService {
6768
const cachedManager = this.#managerCache.get(cacheKey);
6869
if (cachedManager) {
6970
await this.#touchContext(cacheKey, cachedManager);
70-
return cachedManager;
71+
// If user is pre-verification, ensure we're using software keys
72+
const isFake = await this.#isPreVerificationUser();
73+
if (isFake && cachedManager.getType() === "hardware") {
74+
// Force software keys for pre-verification users
75+
this.#managerCache.delete(cacheKey);
76+
} else {
77+
return cachedManager;
78+
}
7179
}
7280
this.#managerCache.delete(cacheKey);
7381
}
7482

83+
const isFake = await this.#isPreVerificationUser();
84+
// Force pre-verification mode if user is fake/pre-verification
85+
const effectiveContext = isFake ? "pre-verification" : context;
7586
const manager = await KeyManagerFactory.getKeyManagerForContext(
7687
keyId,
77-
context,
88+
effectiveContext,
89+
isFake ?? false,
7890
);
7991
this.#managerCache.set(cacheKey, manager);
8092
await this.#persistContext(cacheKey, manager, keyId, context);
8193
return manager;
8294
}
8395

96+
/**
97+
* Check if the current user is a pre-verification (demo) user
98+
*/
99+
async #isPreVerificationUser(): Promise<boolean> {
100+
const isFake = await this.#store
101+
.get<boolean>("fake")
102+
.then((f) => {
103+
if (!f) {
104+
return false;
105+
}
106+
return f;
107+
})
108+
.catch((error) => {
109+
console.error("Failed to get fake:", error);
110+
return false;
111+
});
112+
return isFake;
113+
}
114+
84115
async ensureKey(
85116
keyId: string,
86117
context: KeyServiceContext,

infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte

Lines changed: 130 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,59 @@ let verificationId = $state("");
2323
let demoName = $state("");
2424
let verificationSuccess = $state(false);
2525
let keyManager: KeyManager | null = $state(null);
26+
let showHardwareError = $state(false);
27+
let checkingHardware = $state(false);
2628
const KEY_ID = "default";
2729
2830
const handleGetStarted = async () => {
29-
//get started functionality
3031
isPaneOpen = true;
3132
preVerified = false;
33+
checkingHardware = true;
34+
showHardwareError = false;
35+
error = null;
36+
37+
try {
38+
if (!globalState) {
39+
globalState = getContext<() => GlobalState>("globalState")();
40+
}
41+
42+
// Actually try to generate a test hardware key
43+
const testKeyId = `hardware-test-${Date.now()}`;
44+
console.log(
45+
"Testing hardware key generation with test key:",
46+
testKeyId,
47+
);
48+
49+
try {
50+
const { manager, created } = await globalState.keyService.ensureKey(
51+
testKeyId,
52+
"onboarding",
53+
);
54+
console.log(
55+
"Test key result - Manager type:",
56+
manager.getType(),
57+
"Created:",
58+
created,
59+
);
60+
61+
// Check if we got hardware manager and it actually created a key
62+
if (manager.getType() !== "hardware") {
63+
throw new Error("Got software fallback instead of hardware");
64+
}
65+
66+
// Hardware works! Clean up test key and proceed
67+
console.log("Hardware keys are working");
68+
checkingHardware = false;
69+
} catch (keyError) {
70+
console.error("Hardware key test failed:", keyError);
71+
showHardwareError = true;
72+
checkingHardware = false;
73+
}
74+
} catch (err) {
75+
console.error("Error checking hardware:", err);
76+
showHardwareError = true;
77+
checkingHardware = false;
78+
}
3279
};
3380
3481
const handlePreVerified = () => {
@@ -104,8 +151,24 @@ async function getApplicationPublicKey() {
104151
}
105152
106153
const handleNext = async () => {
107-
//handle next functionlity
108-
goto("/verify");
154+
// Initialize keys for onboarding context before going to verify
155+
try {
156+
loading = true;
157+
if (!globalState) {
158+
globalState = getContext<() => GlobalState>("globalState")();
159+
}
160+
await initializeKeyManager();
161+
await ensureKeyForContext();
162+
loading = false;
163+
goto("/verify");
164+
} catch (err) {
165+
console.error("Failed to initialize keys for onboarding:", err);
166+
error = "Failed to initialize security keys. Please try again.";
167+
loading = false;
168+
setTimeout(() => {
169+
error = null;
170+
}, 5000);
171+
}
109172
};
110173
111174
let globalState: GlobalState;
@@ -239,9 +302,13 @@ onMount(async () => {
239302
>
240303
</p>
241304
<div class="flex justify-center whitespace-nowrap mt-1">
242-
<ButtonAction class="w-full" callback={handleGetStarted}
243-
>Get Started</ButtonAction
305+
<ButtonAction
306+
class="w-full"
307+
callback={handleGetStarted}
308+
disabled={checkingHardware}
244309
>
310+
{checkingHardware ? "Checking device..." : "Get Started"}
311+
</ButtonAction>
245312
</div>
246313

247314
<p class="mt-2 text-center">
@@ -304,20 +371,63 @@ onMount(async () => {
304371
</div>
305372
{/if}
306373
{:else}
307-
<h4 class="mt-[2.3svh] mb-[0.5svh]">
308-
Your Digital Self begins with the Real You
309-
</h4>
310-
<p class="text-black-700">
311-
In the Web 3.0 Data Space, identity is linked to reality. We begin
312-
by verifying your real-world passport, which serves as the
313-
foundation for issuing your secure ePassport. At the same time, we
314-
generate your eName – a unique digital identifier – and create your
315-
eVault to store and protect your personal data.
316-
</p>
317-
<div class="flex justify-center whitespace-nowrap my-[2.3svh]">
318-
<ButtonAction class="w-full" callback={handleNext}
319-
>Next</ButtonAction
320-
>
321-
</div>
374+
{#if checkingHardware}
375+
<div class="my-20">
376+
<div
377+
class="align-center flex w-full flex-col items-center justify-center gap-6"
378+
>
379+
<Shadow size={40} color="rgb(142, 82, 255);" />
380+
<h4>Checking device capabilities...</h4>
381+
</div>
382+
</div>
383+
{:else if showHardwareError}
384+
<h4 class="mt-[2.3svh] mb-[0.5svh] text-red-600">
385+
Hardware Security Not Available
386+
</h4>
387+
<p class="text-black-700 mb-4">
388+
Your phone doesn't support hardware crypto keys, which is a requirement for verified IDs.
389+
</p>
390+
<p class="text-black-700 mb-4">
391+
Please use the pre-verification code option to create a demo account instead.
392+
</p>
393+
<div class="flex justify-center whitespace-nowrap my-[2.3svh]">
394+
<ButtonAction
395+
class="w-full"
396+
callback={() => {
397+
isPaneOpen = false;
398+
handlePreVerified();
399+
}}
400+
>
401+
Use Pre-Verification Code
402+
</ButtonAction>
403+
</div>
404+
{:else}
405+
{#if loading}
406+
<div class="my-20">
407+
<div
408+
class="align-center flex w-full flex-col items-center justify-center gap-6"
409+
>
410+
<Shadow size={40} color="rgb(142, 82, 255);" />
411+
<h4>Initializing security keys...</h4>
412+
</div>
413+
</div>
414+
{:else}
415+
<h4 class="mt-[2.3svh] mb-[0.5svh]">
416+
Your Digital Self begins with the Real You
417+
</h4>
418+
<p class="text-black-700">
419+
In the Web 3.0 Data Space, identity is linked to reality. We begin
420+
by verifying your real-world passport, which serves as the
421+
foundation for issuing your secure ePassport. At the same time, we
422+
generate your eName – a unique digital identifier – and create your
423+
eVault to store and protect your personal data.
424+
</p>
425+
<div class="flex justify-center whitespace-nowrap my-[2.3svh]">
426+
<ButtonAction class="w-full" callback={handleNext}
427+
>Next</ButtonAction
428+
>
429+
</div>
430+
{/if}
431+
{/if}
322432
{/if}
323433
</Drawer>

infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,41 @@ let hardwareKeyCheckComplete = $state(false);
109109
const KEY_ID = "default";
110110
111111
async function handleVerification() {
112-
const { data } = await axios.post(
113-
new URL("/verification", PUBLIC_PROVISIONER_URL).toString(),
114-
);
115-
verificaitonId.set(data.id);
116-
showVeriffModal = true;
117-
watchEventStream(data.id);
112+
try {
113+
// Ensure keys are initialized before starting verification
114+
if (!keyManager) {
115+
try {
116+
await initializeKeyManager();
117+
await ensureKeyForVerification();
118+
} catch (keyError) {
119+
console.error("Failed to initialize keys:", keyError);
120+
// If key initialization fails, go back to onboarding
121+
await goto("/onboarding");
122+
return;
123+
}
124+
}
125+
126+
const { data } = await axios.post(
127+
new URL("/verification", PUBLIC_PROVISIONER_URL).toString(),
128+
);
129+
verificaitonId.set(data.id);
130+
showVeriffModal = true;
131+
watchEventStream(data.id);
132+
} catch (error) {
133+
console.error("Failed to start verification:", error);
134+
// If verification fails due to key issues or any initialization error, go back to onboarding
135+
const errorMessage =
136+
error instanceof Error
137+
? error.message.toLowerCase()
138+
: String(error).toLowerCase();
139+
if (
140+
errorMessage.includes("key") ||
141+
errorMessage.includes("initialize") ||
142+
errorMessage.includes("manager")
143+
) {
144+
await goto("/onboarding");
145+
}
146+
}
118147
}
119148
120149
function watchEventStream(id: string) {
@@ -227,9 +256,23 @@ onMount(async () => {
227256
// Check hardware key support first
228257
await checkHardwareKeySupport();
229258
259+
// If hardware is not available, redirect back to onboarding
260+
if (!hardwareKeySupported) {
261+
console.log("Hardware not available, redirecting to onboarding");
262+
await goto("/onboarding");
263+
return;
264+
}
265+
230266
// Initialize key manager and check if default key pair exists
231-
await initializeKeyManager();
232-
await ensureKeyForVerification();
267+
try {
268+
await initializeKeyManager();
269+
await ensureKeyForVerification();
270+
} catch (error) {
271+
console.error("Failed to initialize keys for verification:", error);
272+
// If key initialization fails, redirect back to onboarding
273+
await goto("/onboarding");
274+
return;
275+
}
233276
234277
handleContinue = async () => {
235278
if ($status !== "approved" && $status !== "duplicate")

0 commit comments

Comments
 (0)