From b1ac164c6a40f0dc9c48fbd6b68bbfdf7501b0d0 Mon Sep 17 00:00:00 2001 From: Merul Dhiman Date: Sun, 7 Dec 2025 11:19:01 +0530 Subject: [PATCH 1/9] chore: test script --- .../src/routes/(app)/scan-qr/scanLogic.ts | 37 +------- .../signature-validator/package.json | 5 +- .../signature-validator/test-example.ts | 90 +++++++++++++++++++ .../signature-validator/test-signature.ts | 78 ++++++++++++++++ .../signature-validator/tsconfig.json | 2 +- .../src/controllers/AuthController.ts | 2 + 6 files changed, 179 insertions(+), 35 deletions(-) create mode 100644 infrastructure/signature-validator/test-example.ts create mode 100644 infrastructure/signature-validator/test-signature.ts diff --git a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts index 8f0917bbe..91e6ef0ef 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts +++ b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts @@ -252,11 +252,7 @@ export function createScanLogic({ throw new Error("Failed to get W3ID"); } - const sessionPayload = JSON.stringify({ - session: get(session), - ename: vault.ename, - timestamp: Date.now(), - }); + const sessionPayload = get(session) as string; const signature = await globalState.keyService.signPayload( vault.ename, @@ -458,15 +454,6 @@ export function createScanLogic({ } signingData.set(decodedData); - console.log("🔍 DEBUG: Decoded signing data:", decodedData); - console.log( - "🔍 DEBUG: Data keys:", - Object.keys(decodedData || {}), - ); - console.log( - "🔍 DEBUG: Is poll request?", - !!(decodedData?.pollId && decodedData?.voteData), - ); isSigningRequest.set(true); signingDrawerOpen.set(true); @@ -583,33 +570,17 @@ export function createScanLogic({ throw new Error("Failed to get W3ID"); } - let messageToSign: string; + const messageToSign= currentSigningSessionId; - if (currentSigningData.pollId && currentSigningData.voteData) { - messageToSign = JSON.stringify({ - pollId: currentSigningData.pollId, - voteData: currentSigningData.voteData, - userId: currentSigningData.userId, - sessionId: currentSigningSessionId, - timestamp: Date.now(), - }); - } else { - messageToSign = JSON.stringify({ - message: currentSigningData.message, - sessionId: currentSigningData.sessionId, - timestamp: Date.now(), - }); - } console.log( - "🔐 Starting cryptographic signing process with KeyManager...", + "🔐 Starting cryptographic signing pross with KeyManager...", ); - console.log("✍️ Signing message:", messageToSign); const signature = await globalState.keyService.signPayload( vault.ename, "signing", - messageToSign, + currentSigningSessionId ); console.log("✅ Message signed successfully"); diff --git a/infrastructure/signature-validator/package.json b/infrastructure/signature-validator/package.json index e9e0ca638..402f33c15 100644 --- a/infrastructure/signature-validator/package.json +++ b/infrastructure/signature-validator/package.json @@ -7,7 +7,9 @@ "scripts": { "build": "tsc", "test": "vitest", - "dev": "tsc --watch" + "dev": "tsc --watch", + "test:signature": "tsx test-example.ts", + "test:signature:custom": "tsx test-signature.ts" }, "dependencies": { "axios": "^1.6.7", @@ -16,6 +18,7 @@ "devDependencies": { "@types/node": "^20.11.24", "typescript": "^5.3.3", + "tsx": "^4.7.0", "vitest": "^1.6.1" } } \ No newline at end of file diff --git a/infrastructure/signature-validator/test-example.ts b/infrastructure/signature-validator/test-example.ts new file mode 100644 index 000000000..ad6543798 --- /dev/null +++ b/infrastructure/signature-validator/test-example.ts @@ -0,0 +1,90 @@ +/** + * Example test script with the provided signature and eName + * + * This script tests the signature validation with: + * - Signature: CL+njc5oRjD9C3KMgN+K+PPdsLsEI9z++it6t+92IZuMe1GRLrN779sBz5RVhoHbGbgDD84x/cb2lx/JCg8Fuw== + * - eName: @d12057f3-7447-5c87-b12b-32f59ea15294 + * + * Note: You need to provide the payload that was signed. Common payloads for authentication: + * - Session ID + * - Offer string (w3ds://auth?redirect=...&session=...&platform=...) + * - Combination of session + ename + */ + +import { verifySignature } from "./src/index"; + +const SIGNATURE = "CL+njc5oRjD9C3KMgN+K+PPdsLsEI9z++it6t+92IZuMe1GRLrN779sBz5RVhoHbGbgDD84x/cb2lx/JCg8Fuw=="; +const ENAME = "@d12057f3-7447-5c87-b12b-32f59ea15294"; + +// TODO: Replace with the actual payload that was signed +// Common examples: +// - Session ID: "some-session-id" +// - Offer string: "w3ds://auth?redirect=http://localhost:9888/api/auth&session=xxx&platform=ecurrency" +// - Custom message: "your-message-here" +const PAYLOAD = process.env.TEST_PAYLOAD || "test-payload-change-me"; + +// Registry URL - defaults to staging, can be overridden with REGISTRY_BASE_URL env var +const REGISTRY_BASE_URL = process.env.REGISTRY_BASE_URL || "https://registry.staging.metastate.foundation"; + +async function testSignature() { + console.log("=".repeat(70)); + console.log("Signature Validation Test"); + console.log("=".repeat(70)); + console.log(`eName: ${ENAME}`); + console.log(`Signature: ${SIGNATURE.substring(0, 50)}...`); + console.log(`Payload: ${PAYLOAD}`); + console.log(`Registry URL: ${REGISTRY_BASE_URL}`); + console.log("=".repeat(70)); + console.log(""); + + try { + console.log("Fetching public key from eVault..."); + console.log("Verifying signature..."); + console.log(""); + + const result = await verifySignature({ + eName: ENAME, + signature: SIGNATURE, + payload: PAYLOAD, + registryBaseUrl: REGISTRY_BASE_URL, + }); + + if (result.valid) { + console.log("✅ SUCCESS: Signature is VALID"); + console.log(""); + if (result.publicKey) { + console.log(`Public Key: ${result.publicKey}`); + } + process.exit(0); + } else { + console.log("❌ FAILED: Signature is INVALID"); + console.log(""); + if (result.error) { + console.log(`Error: ${result.error}`); + } + console.log(""); + console.log("Note: Make sure the payload matches exactly what was signed."); + console.log(" Set TEST_PAYLOAD environment variable to test different payloads:"); + console.log(' TEST_PAYLOAD="your-payload" npm run test:signature'); + process.exit(1); + } + } catch (error) { + console.error("❌ ERROR during verification:"); + console.error(""); + if (error instanceof Error) { + console.error(`Message: ${error.message}`); + if (error.stack) { + console.error(""); + console.error("Stack trace:"); + console.error(error.stack); + } + } else { + console.error(String(error)); + } + console.error(""); + process.exit(1); + } +} + +testSignature(); + diff --git a/infrastructure/signature-validator/test-signature.ts b/infrastructure/signature-validator/test-signature.ts new file mode 100644 index 000000000..d8cc34461 --- /dev/null +++ b/infrastructure/signature-validator/test-signature.ts @@ -0,0 +1,78 @@ +#!/usr/bin/env ts-node + +/** + * Test script to validate signatures using the signature-validator library + * + * Usage: + * ts-node test-signature.ts [registryBaseUrl] + * + * Example: + * ts-node test-signature.ts "CL+njc5oRjD9C3KMgN+K+PPdsLsEI9z++it6t+92IZuMe1GRLrN779sBz5RVhoHbGbgDD84x/cb2lx/JCg8Fuw==" "@d12057f3-7447-5c87-b12b-32f59ea15294" "test payload" + */ + +import { verifySignature } from "./src/index"; + +async function main() { + // Parse command line arguments + const args = process.argv.slice(2); + + if (args.length < 3) { + console.error("Usage: ts-node test-signature.ts [registryBaseUrl]"); + console.error(""); + console.error("Example:"); + console.error(' ts-node test-signature.ts "CL+njc5oRjD9C3KMgN+K+PPdsLsEI9z++it6t+92IZuMe1GRLrN779sBz5RVhoHbGbgDD84x/cb2lx/JCg8Fuw==" "@d12057f3-7447-5c87-b12b-32f59ea15294" "test payload"'); + process.exit(1); + } + + const [signature, ename, payload, registryBaseUrl] = args; + + // Default registry URL + const registryUrl = registryBaseUrl || + process.env.REGISTRY_BASE_URL || + "https://registry.staging.metastate.foundation"; + + console.log("=".repeat(60)); + console.log("Signature Validation Test"); + console.log("=".repeat(60)); + console.log(`eName: ${ename}`); + console.log(`Signature: ${signature}`); + console.log(`Payload: ${payload}`); + console.log(`Registry URL: ${registryUrl}`); + console.log("=".repeat(60)); + console.log(""); + + try { + console.log("Verifying signature..."); + const result = await verifySignature({ + eName, + signature, + payload, + registryBaseUrl: registryUrl, + }); + + console.log(""); + if (result.valid) { + console.log("✅ Signature is VALID"); + if (result.publicKey) { + console.log(`Public Key: ${result.publicKey}`); + } + } else { + console.log("❌ Signature is INVALID"); + if (result.error) { + console.log(`Error: ${result.error}`); + } + } + console.log(""); + + process.exit(result.valid ? 0 : 1); + } catch (error) { + console.error(""); + console.error("❌ Error during verification:"); + console.error(error instanceof Error ? error.message : String(error)); + console.error(""); + process.exit(1); + } +} + +main(); + diff --git a/infrastructure/signature-validator/tsconfig.json b/infrastructure/signature-validator/tsconfig.json index 23b617585..3f3095b16 100644 --- a/infrastructure/signature-validator/tsconfig.json +++ b/infrastructure/signature-validator/tsconfig.json @@ -12,7 +12,7 @@ "sourceMap": true, "declaration": true }, - "include": ["src/**/*"], + "include": ["src/**/*", "test-*.ts"], "exclude": ["node_modules", "dist"] } diff --git a/platforms/eCurrency-api/src/controllers/AuthController.ts b/platforms/eCurrency-api/src/controllers/AuthController.ts index 545d13388..6cd616963 100644 --- a/platforms/eCurrency-api/src/controllers/AuthController.ts +++ b/platforms/eCurrency-api/src/controllers/AuthController.ts @@ -56,6 +56,8 @@ export class AuthController { try { const { ename, session, w3id, signature } = req.body; + console.log(signature, ename) + if (!ename) { return res.status(400).json({ error: "ename is required" }); } From 48d54df62558d7deecf226ee801321259176068a Mon Sep 17 00:00:00 2001 From: Merul Dhiman Date: Sun, 7 Dec 2025 11:33:36 +0530 Subject: [PATCH 2/9] fix: test script --- .../signature-validator/src/index.ts | 29 ++++++++++++----- .../signature-validator/test-example.ts | 32 +++++++------------ .../src/controllers/AuthController.ts | 2 +- pnpm-lock.yaml | 3 ++ 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/infrastructure/signature-validator/src/index.ts b/infrastructure/signature-validator/src/index.ts index 5690b0eb8..8a3f431d7 100644 --- a/infrastructure/signature-validator/src/index.ts +++ b/infrastructure/signature-validator/src/index.ts @@ -1,5 +1,14 @@ import axios from "axios"; -import { base58btc } from "multiformats/bases/base58"; + +// Lazy initialization for base58btc to handle ESM module resolution +let base58btcModule: { base58btc: { decode: (input: string) => Uint8Array } } | null = null; + +async function getBase58btc() { + if (!base58btcModule) { + base58btcModule = await import("multiformats/bases/base58"); + } + return base58btcModule.base58btc; +} /** * Options for signature verification @@ -32,7 +41,7 @@ export interface VerifySignatureResult { * Supports 'z' prefix for base58btc or hex encoding * Based on the format used in SoftwareKeyManager: 'z' + hex */ -function decodeMultibasePublicKey(multibaseKey: string): Uint8Array { +async function decodeMultibasePublicKey(multibaseKey: string): Promise { if (!multibaseKey.startsWith("z")) { throw new Error("Public key must start with 'z' multibase prefix"); } @@ -58,6 +67,7 @@ function decodeMultibasePublicKey(multibaseKey: string): Uint8Array { // Try base58btc (standard multibase 'z' prefix) try { + const base58btc = await getBase58btc(); return base58btc.decode(encoded); } catch (error) { throw new Error( @@ -67,20 +77,23 @@ function decodeMultibasePublicKey(multibaseKey: string): Uint8Array { } /** - * Decodes a multibase-encoded signature - * Supports base58btc or base64 + * Decodes a signature + * Supports: + * - Multibase base58btc (starts with 'z') + * - Base64 (default for software keys) */ -function decodeSignature(signature: string): Uint8Array { +async function decodeSignature(signature: string): Promise { // If it starts with 'z', it's multibase base58btc if (signature.startsWith("z")) { try { + const base58btc = await getBase58btc(); return base58btc.decode(signature.slice(1)); } catch (error) { throw new Error(`Failed to decode multibase signature: ${error instanceof Error ? error.message : String(error)}`); } } - // Otherwise, try base64 (software keys return base64) + // Default: decode as base64 (software keys return base64-encoded signatures) try { const binaryString = atob(signature); const bytes = new Uint8Array(binaryString.length); @@ -189,7 +202,7 @@ export async function verifySignature( const publicKeyMultibase = await getPublicKey(eName, registryBaseUrl); // Decode the public key - const publicKeyBytes = decodeMultibasePublicKey(publicKeyMultibase); + const publicKeyBytes = await decodeMultibasePublicKey(publicKeyMultibase); // Import the public key for Web Crypto API // The public key is in SPKI format (SubjectPublicKeyInfo) @@ -208,7 +221,7 @@ export async function verifySignature( ); // Decode the signature - const signatureBytes = decodeSignature(signature); + const signatureBytes = await decodeSignature(signature); // Convert payload to ArrayBuffer const payloadBuffer = new TextEncoder().encode(payload); diff --git a/infrastructure/signature-validator/test-example.ts b/infrastructure/signature-validator/test-example.ts index ad6543798..8bf68d85f 100644 --- a/infrastructure/signature-validator/test-example.ts +++ b/infrastructure/signature-validator/test-example.ts @@ -1,30 +1,23 @@ /** - * Example test script with the provided signature and eName + * Test script to validate signatures using the signature-validator library * - * This script tests the signature validation with: - * - Signature: CL+njc5oRjD9C3KMgN+K+PPdsLsEI9z++it6t+92IZuMe1GRLrN779sBz5RVhoHbGbgDD84x/cb2lx/JCg8Fuw== - * - eName: @d12057f3-7447-5c87-b12b-32f59ea15294 - * - * Note: You need to provide the payload that was signed. Common payloads for authentication: - * - Session ID - * - Offer string (w3ds://auth?redirect=...&session=...&platform=...) - * - Combination of session + ename + * Edit the variables below to test different signatures, eNames, and payloads. */ import { verifySignature } from "./src/index"; -const SIGNATURE = "CL+njc5oRjD9C3KMgN+K+PPdsLsEI9z++it6t+92IZuMe1GRLrN779sBz5RVhoHbGbgDD84x/cb2lx/JCg8Fuw=="; +// ============================================================================ +// CONFIGURATION - Edit these values to test different signatures +// ============================================================================ + +const SIGNATURE = "Aa+ggCam4LJXA5TfXZpiiQcdAwVOln6JY8IUBVBPJNkRLLHIejIzb4vO58xl0k26o/LlKOVRP/Aw6qXncPEmpg==" const ENAME = "@d12057f3-7447-5c87-b12b-32f59ea15294"; +const PAYLOAD = "b156890e-0dbf-416f-bc25-eae93929cbf8"; // Replace with the actual payload that was signed -// TODO: Replace with the actual payload that was signed -// Common examples: -// - Session ID: "some-session-id" -// - Offer string: "w3ds://auth?redirect=http://localhost:9888/api/auth&session=xxx&platform=ecurrency" -// - Custom message: "your-message-here" -const PAYLOAD = process.env.TEST_PAYLOAD || "test-payload-change-me"; +// Registry URL - defaults to staging +const REGISTRY_BASE_URL = process.env.REGISTRY_BASE_URL || "http://localhost:4321"; -// Registry URL - defaults to staging, can be overridden with REGISTRY_BASE_URL env var -const REGISTRY_BASE_URL = process.env.REGISTRY_BASE_URL || "https://registry.staging.metastate.foundation"; +// ============================================================================ async function testSignature() { console.log("=".repeat(70)); @@ -64,8 +57,7 @@ async function testSignature() { } console.log(""); console.log("Note: Make sure the payload matches exactly what was signed."); - console.log(" Set TEST_PAYLOAD environment variable to test different payloads:"); - console.log(' TEST_PAYLOAD="your-payload" npm run test:signature'); + console.log(" Edit the PAYLOAD variable at the top of the script to test different payloads."); process.exit(1); } } catch (error) { diff --git a/platforms/eCurrency-api/src/controllers/AuthController.ts b/platforms/eCurrency-api/src/controllers/AuthController.ts index 6cd616963..c6c4bf87e 100644 --- a/platforms/eCurrency-api/src/controllers/AuthController.ts +++ b/platforms/eCurrency-api/src/controllers/AuthController.ts @@ -56,7 +56,7 @@ export class AuthController { try { const { ename, session, w3id, signature } = req.body; - console.log(signature, ename) + console.log(signature, ename, session) if (!ename) { return res.status(400).json({ error: "ename is required" }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 53fc32f8d..2f71ac477 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -487,6 +487,9 @@ importers: '@types/node': specifier: ^20.11.24 version: 20.16.11 + tsx: + specifier: ^4.7.0 + version: 4.20.6 typescript: specifier: ^5.3.3 version: 5.8.2 From 6db1bf256aae7710542c515406c94ed54f62172a Mon Sep 17 00:00:00 2001 From: Merul Dhiman Date: Sun, 7 Dec 2025 11:49:27 +0530 Subject: [PATCH 3/9] chore: debug log --- .../src/lib/crypto/HardwareKeyManager.ts | 33 +++++++++++++- .../src/lib/crypto/SoftwareKeyManager.ts | 32 ++++++++++++- .../src/lib/global/controllers/key.ts | 33 ++++++++++++++ .../signature-validator/src/index.ts | 45 ++++++++++++++----- .../signature-validator/test-example.ts | 38 ++++++++++++---- .../signature-validator/tsconfig.json | 4 +- 6 files changed, 161 insertions(+), 24 deletions(-) diff --git a/infrastructure/eid-wallet/src/lib/crypto/HardwareKeyManager.ts b/infrastructure/eid-wallet/src/lib/crypto/HardwareKeyManager.ts index 98f9d9b03..2669cdd97 100644 --- a/infrastructure/eid-wallet/src/lib/crypto/HardwareKeyManager.ts +++ b/infrastructure/eid-wallet/src/lib/crypto/HardwareKeyManager.ts @@ -64,11 +64,40 @@ export class HardwareKeyManager implements KeyManager { async signPayload(keyId: string, payload: string): Promise { try { + console.log("=".repeat(70)); + console.log("🔐 [HardwareKeyManager] signPayload called"); + console.log("=".repeat(70)); + console.log(`Key ID: ${keyId}`); + console.log(`Payload: "${payload}"`); + console.log(`Payload length: ${payload.length} bytes`); + const payloadHex = Array.from(new TextEncoder().encode(payload)) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + console.log(`Payload (hex): ${payloadHex}`); + + // Get and log the public key + try { + const publicKey = await this.getPublicKey(keyId); + if (publicKey) { + console.log(`Public key: ${publicKey.substring(0, 60)}...`); + console.log(`Public key (full): ${publicKey}`); + } else { + console.log("⚠️ Public key not available"); + } + } catch (error) { + console.log(`⚠️ Failed to get public key: ${error instanceof Error ? error.message : String(error)}`); + } + + console.log("Signing with hardware key..."); const signature = await hwSignPayload(keyId, payload); - console.log(`Hardware signature created for ${keyId}`); + console.log(`✅ Hardware signature created for ${keyId}`); + console.log(`Signature: ${signature.substring(0, 50)}...`); + console.log(`Signature (full): ${signature}`); + console.log(`Signature length: ${signature.length} chars`); + console.log("=".repeat(70)); return signature; } catch (error) { - console.error("Hardware signing failed:", error); + console.error("❌ Hardware signing failed:", error); throw new KeyManagerError( "Failed to sign payload with hardware key", KeyManagerErrorCodes.SIGNING_FAILED, diff --git a/infrastructure/eid-wallet/src/lib/crypto/SoftwareKeyManager.ts b/infrastructure/eid-wallet/src/lib/crypto/SoftwareKeyManager.ts index b7e703449..1256cda23 100644 --- a/infrastructure/eid-wallet/src/lib/crypto/SoftwareKeyManager.ts +++ b/infrastructure/eid-wallet/src/lib/crypto/SoftwareKeyManager.ts @@ -115,6 +115,17 @@ export class SoftwareKeyManager implements KeyManager { async signPayload(keyId: string, payload: string): Promise { try { + console.log("=".repeat(70)); + console.log("🔐 [SoftwareKeyManager] signPayload called"); + console.log("=".repeat(70)); + console.log(`Key ID: ${keyId}`); + console.log(`Payload: "${payload}"`); + console.log(`Payload length: ${payload.length} bytes`); + const payloadHex = Array.from(new TextEncoder().encode(payload)) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + console.log(`Payload (hex): ${payloadHex}`); + const keyPair = await this.getKeyPair(keyId); if (!keyPair) { throw new KeyManagerError( @@ -124,10 +135,16 @@ export class SoftwareKeyManager implements KeyManager { ); } + // Log the public key that corresponds to this private key + console.log(`Public key (base64): ${keyPair.publicKey.substring(0, 60)}...`); + console.log(`Public key (full): ${keyPair.publicKey}`); + // Import the private key const privateKeyBuffer = this.base64ToArrayBuffer( keyPair.privateKey, ); + console.log(`Private key buffer length: ${privateKeyBuffer.byteLength} bytes`); + const privateKey = await crypto.subtle.importKey( "pkcs8", privateKeyBuffer, @@ -138,11 +155,15 @@ export class SoftwareKeyManager implements KeyManager { false, ["sign"], ); + console.log("✅ Private key imported successfully"); // Convert payload to ArrayBuffer const payloadBuffer = new TextEncoder().encode(payload); + console.log(`Payload buffer length: ${payloadBuffer.byteLength} bytes`); + console.log(`Payload buffer (hex): ${this.arrayBufferToHex(payloadBuffer)}`); // Sign the payload + console.log("Signing with ECDSA P-256, SHA-256..."); const signature = await crypto.subtle.sign( { name: "ECDSA", @@ -152,12 +173,19 @@ export class SoftwareKeyManager implements KeyManager { payloadBuffer, ); + console.log(`Signature buffer length: ${signature.byteLength} bytes`); + console.log(`Signature buffer (hex): ${this.arrayBufferToHex(signature)}`); + // Convert signature to base64 string const signatureString = this.arrayBufferToBase64(signature); - console.log(`Software signature created for ${keyId}`); + console.log(`✅ Software signature created for ${keyId}`); + console.log(`Signature (base64): ${signatureString.substring(0, 50)}...`); + console.log(`Signature (full): ${signatureString}`); + console.log(`Signature length: ${signatureString.length} chars`); + console.log("=".repeat(70)); return signatureString; } catch (error) { - console.error("Software signing failed:", error); + console.error("❌ Software signing failed:", error); if (error instanceof KeyManagerError) { throw error; } diff --git a/infrastructure/eid-wallet/src/lib/global/controllers/key.ts b/infrastructure/eid-wallet/src/lib/global/controllers/key.ts index a2d69aa82..7b84a5f9f 100644 --- a/infrastructure/eid-wallet/src/lib/global/controllers/key.ts +++ b/infrastructure/eid-wallet/src/lib/global/controllers/key.ts @@ -145,8 +145,41 @@ export class KeyService { context: KeyServiceContext, payload: string, ): Promise { + console.log("=".repeat(70)); + console.log("🔐 [KeyService] signPayload called"); + console.log("=".repeat(70)); + console.log(`Key ID: ${keyId}`); + console.log(`Context: ${context}`); + console.log(`Payload: "${payload}"`); + console.log(`Payload length: ${payload.length} bytes`); + const payloadHex = Array.from(new TextEncoder().encode(payload)) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + console.log(`Payload (hex): ${payloadHex}`); + const manager = await this.getManager(keyId, context); + const managerType = manager.getType(); + console.log(`Manager type: ${managerType}`); + + // Get and log the public key that will be used for signing + try { + const publicKey = await manager.getPublicKey(keyId); + if (publicKey) { + console.log(`Public key: ${publicKey.substring(0, 60)}...`); + console.log(`Public key (full): ${publicKey}`); + } else { + console.log("⚠️ Public key not available"); + } + } catch (error) { + console.log(`⚠️ Failed to get public key: ${error instanceof Error ? error.message : String(error)}`); + } + + console.log("=".repeat(70)); const signature = await manager.signPayload(keyId, payload); + console.log(`✅ [KeyService] Signature created: ${signature.substring(0, 50)}...`); + console.log(`Signature length: ${signature.length} chars`); + console.log("=".repeat(70)); + await this.#touchContext(this.#getCacheKey(keyId, context), manager); return signature; } diff --git a/infrastructure/signature-validator/src/index.ts b/infrastructure/signature-validator/src/index.ts index 8a3f431d7..25504b9b1 100644 --- a/infrastructure/signature-validator/src/index.ts +++ b/infrastructure/signature-validator/src/index.ts @@ -199,37 +199,61 @@ export async function verifySignature( } // Get public key from eVault + console.log("[DEBUG] Step 1: Fetching public key from eVault..."); const publicKeyMultibase = await getPublicKey(eName, registryBaseUrl); + console.log(`[DEBUG] Public key retrieved (full): ${publicKeyMultibase}`); + console.log(`[DEBUG] Public key starts with 'z': ${publicKeyMultibase.startsWith('z')}`); // Decode the public key + console.log("[DEBUG] Step 2: Decoding public key..."); const publicKeyBytes = await decodeMultibasePublicKey(publicKeyMultibase); + console.log(`[DEBUG] Public key bytes length: ${publicKeyBytes.length}`); + console.log(`[DEBUG] Public key bytes (hex, first 100 chars): ${Buffer.from(publicKeyBytes).toString('hex').substring(0, 100)}...`); + console.log(`[DEBUG] Public key bytes (base64): ${Buffer.from(publicKeyBytes).toString('base64')}`); // Import the public key for Web Crypto API // The public key is in SPKI format (SubjectPublicKeyInfo) // Create a new ArrayBuffer from the Uint8Array const publicKeyBuffer = new Uint8Array(publicKeyBytes).buffer; - const publicKey = await crypto.subtle.importKey( - "spki", - publicKeyBuffer, - { - name: "ECDSA", - namedCurve: "P-256", - }, - false, - ["verify"] - ); + console.log("[DEBUG] Step 3: Importing public key into Web Crypto API..."); + let publicKey; + try { + publicKey = await crypto.subtle.importKey( + "spki", + publicKeyBuffer, + { + name: "ECDSA", + namedCurve: "P-256", + }, + false, + ["verify"] + ); + console.log("[DEBUG] Public key imported successfully"); + } catch (importError) { + console.error(`[DEBUG] Failed to import public key: ${importError instanceof Error ? importError.message : String(importError)}`); + throw importError; + } // Decode the signature + console.log("[DEBUG] Step 4: Decoding signature..."); const signatureBytes = await decodeSignature(signature); + console.log(`[DEBUG] Signature bytes length: ${signatureBytes.length}`); + console.log(`[DEBUG] Signature bytes (hex): ${Buffer.from(signatureBytes).toString('hex')}`); // Convert payload to ArrayBuffer + console.log("[DEBUG] Step 5: Encoding payload..."); const payloadBuffer = new TextEncoder().encode(payload); + console.log(`[DEBUG] Payload: "${payload}"`); + console.log(`[DEBUG] Payload bytes length: ${payloadBuffer.byteLength}`); + console.log(`[DEBUG] Payload bytes (hex): ${Buffer.from(payloadBuffer).toString('hex')}`); // Create a new ArrayBuffer from the signature Uint8Array const signatureBuffer = new Uint8Array(signatureBytes).buffer; // Verify the signature + console.log("[DEBUG] Step 6: Verifying signature with Web Crypto API..."); + console.log(`[DEBUG] Algorithm: ECDSA with P-256 curve, SHA-256 hash`); const isValid = await crypto.subtle.verify( { name: "ECDSA", @@ -239,6 +263,7 @@ export async function verifySignature( signatureBuffer, payloadBuffer ); + console.log(`[DEBUG] Verification result: ${isValid ? "VALID" : "INVALID"}`); return { valid: isValid, diff --git a/infrastructure/signature-validator/test-example.ts b/infrastructure/signature-validator/test-example.ts index 8bf68d85f..594ef7810 100644 --- a/infrastructure/signature-validator/test-example.ts +++ b/infrastructure/signature-validator/test-example.ts @@ -10,9 +10,9 @@ import { verifySignature } from "./src/index"; // CONFIGURATION - Edit these values to test different signatures // ============================================================================ -const SIGNATURE = "Aa+ggCam4LJXA5TfXZpiiQcdAwVOln6JY8IUBVBPJNkRLLHIejIzb4vO58xl0k26o/LlKOVRP/Aw6qXncPEmpg==" +const SIGNATURE = "WS/lLXhE6P++PPgyud1rEkKBUNgaKyvxunDadKltFq+beBJXcowf+0aneGyRB3hK+Cu6SRNL7e8QYjdpOZNvcQ==" const ENAME = "@d12057f3-7447-5c87-b12b-32f59ea15294"; -const PAYLOAD = "b156890e-0dbf-416f-bc25-eae93929cbf8"; // Replace with the actual payload that was signed +const PAYLOAD = "03edefec-92ab-4ebd-a758-3bf60547c07e"; // Replace with the actual payload that was signed // Registry URL - defaults to staging const REGISTRY_BASE_URL = process.env.REGISTRY_BASE_URL || "http://localhost:4321"; @@ -31,16 +31,27 @@ async function testSignature() { console.log(""); try { - console.log("Fetching public key from eVault..."); - console.log("Verifying signature..."); - console.log(""); - + console.log("Step 1: Fetching public key from eVault..."); const result = await verifySignature({ eName: ENAME, signature: SIGNATURE, payload: PAYLOAD, registryBaseUrl: REGISTRY_BASE_URL, }); + + console.log(""); + console.log("=".repeat(70)); + console.log("DEBUG INFO"); + console.log("=".repeat(70)); + console.log(`Payload length: ${PAYLOAD.length} bytes`); + console.log(`Payload (hex): ${Buffer.from(PAYLOAD, 'utf8').toString('hex')}`); + console.log(`Signature length: ${SIGNATURE.length} chars`); + console.log(`Signature (first 20 chars): ${SIGNATURE.substring(0, 20)}...`); + if (result.publicKey) { + console.log(`Public key retrieved: ${result.publicKey.substring(0, 50)}...`); + } + console.log("=".repeat(70)); + console.log(""); if (result.valid) { console.log("✅ SUCCESS: Signature is VALID"); @@ -56,8 +67,19 @@ async function testSignature() { console.log(`Error: ${result.error}`); } console.log(""); - console.log("Note: Make sure the payload matches exactly what was signed."); - console.log(" Edit the PAYLOAD variable at the top of the script to test different payloads."); + console.log("=".repeat(70)); + console.log("TROUBLESHOOTING"); + console.log("=".repeat(70)); + console.log("Possible issues:"); + console.log("1. Public key from eVault doesn't match the key used to sign"); + console.log("2. Payload encoding mismatch (check for whitespace, case, etc.)"); + console.log("3. Signature was created with a different key than stored in eVault"); + console.log(""); + console.log("To debug:"); + console.log("- Verify the public key in eVault matches the signing key"); + console.log("- Check if payload has any hidden characters or encoding differences"); + console.log("- Ensure the signature was created with the same key stored in eVault"); + console.log("=".repeat(70)); process.exit(1); } } catch (error) { diff --git a/infrastructure/signature-validator/tsconfig.json b/infrastructure/signature-validator/tsconfig.json index 3f3095b16..0a2a2ea54 100644 --- a/infrastructure/signature-validator/tsconfig.json +++ b/infrastructure/signature-validator/tsconfig.json @@ -12,7 +12,7 @@ "sourceMap": true, "declaration": true }, - "include": ["src/**/*", "test-*.ts"], - "exclude": ["node_modules", "dist"] + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "test-*.ts"] } From e401537489bf2116f76c0e41bbe6721ca863b8ce Mon Sep 17 00:00:00 2001 From: Merul Dhiman Date: Sun, 7 Dec 2025 11:59:20 +0530 Subject: [PATCH 4/9] fix: key id --- .../src/lib/global/controllers/evault.ts | 11 +++++ .../src/routes/(app)/scan-qr/scanLogic.ts | 45 +++++++++++++++---- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/infrastructure/eid-wallet/src/lib/global/controllers/evault.ts b/infrastructure/eid-wallet/src/lib/global/controllers/evault.ts index 349f96186..f8efb8bac 100644 --- a/infrastructure/eid-wallet/src/lib/global/controllers/evault.ts +++ b/infrastructure/eid-wallet/src/lib/global/controllers/evault.ts @@ -138,6 +138,14 @@ export class VaultController { const isFake = await this.#userController.isFake; const context = isFake ? "pre-verification" : "onboarding"; + console.log("=".repeat(70)); + console.log("🔄 [VaultController] syncPublicKey called"); + console.log("=".repeat(70)); + console.log(`eName: ${eName}`); + console.log(`Key ID for sync: ${KEY_ID}`); + console.log(`Context for sync: ${context}`); + console.log(`Is fake/pre-verification: ${isFake}`); + // Get public key using the same method as getApplicationPublicKey() in onboarding/verify let publicKey: string | undefined; try { @@ -145,6 +153,8 @@ export class VaultController { KEY_ID, context, ); + console.log(`Public key retrieved: ${publicKey?.substring(0, 60)}...`); + console.log(`Public key (full): ${publicKey}`); } catch (error) { console.error( `Failed to get public key for ${KEY_ID} with context ${context}:`, @@ -159,6 +169,7 @@ export class VaultController { ); return; } + console.log("=".repeat(70)); // Get authentication token from environment variable const authToken = PUBLIC_EID_WALLET_TOKEN || null; diff --git a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts index 91e6ef0ef..124aec059 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts +++ b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts @@ -238,9 +238,21 @@ export function createScanLogic({ authLoading.set(true); try { + const KEY_ID = "default"; + const isFake = await globalState.userController.isFake; + const signingContext = isFake ? "pre-verification" : "onboarding"; + + console.log("=".repeat(70)); + console.log("🔐 [scanLogic] handleAuth - Preparing to sign payload"); + console.log("=".repeat(70)); + console.log(`⚠️ Using keyId: ${KEY_ID} (NOT ${vault.ename})`); + console.log(`⚠️ Using context: ${signingContext} (NOT "signing")`); + console.log(`⚠️ This ensures we use the SAME key that was synced to eVault`); + console.log("=".repeat(70)); + const { created } = await globalState.keyService.ensureKey( - vault.ename, - "signing", + KEY_ID, + signingContext, ); console.log( "Key generation result for signing:", @@ -255,8 +267,8 @@ export function createScanLogic({ const sessionPayload = get(session) as string; const signature = await globalState.keyService.signPayload( - vault.ename, - "signing", + KEY_ID, + signingContext, sessionPayload, ); @@ -556,9 +568,24 @@ export function createScanLogic({ throw new Error("No vault available for signing"); } + // ⚠️ CRITICAL: Use the SAME keyId and context that was synced to eVault! + // The key synced to eVault uses keyId="default" with context="onboarding" or "pre-verification" + // NOT vault.ename with context="signing"! + const KEY_ID = "default"; + const isFake = await globalState.userController.isFake; + const signingContext = isFake ? "pre-verification" : "onboarding"; + + console.log("=".repeat(70)); + console.log("🔐 [scanLogic] Preparing to sign payload"); + console.log("=".repeat(70)); + console.log(`⚠️ Using keyId: ${KEY_ID} (NOT ${vault.ename})`); + console.log(`⚠️ Using context: ${signingContext} (NOT "signing")`); + console.log(`⚠️ This ensures we use the SAME key that was synced to eVault`); + console.log("=".repeat(70)); + const { created } = await globalState.keyService.ensureKey( - vault.ename, - "signing", + KEY_ID, + signingContext, ); console.log( "Key generation result for signing:", @@ -574,12 +601,12 @@ export function createScanLogic({ console.log( - "🔐 Starting cryptographic signing pross with KeyManager...", + "🔐 Starting cryptographic signing process with KeyManager...", ); const signature = await globalState.keyService.signPayload( - vault.ename, - "signing", + KEY_ID, + signingContext, currentSigningSessionId ); console.log("✅ Message signed successfully"); From 8a8f8e93c2586f41a29143565f29f37d6be88c28 Mon Sep 17 00:00:00 2001 From: Merul Dhiman Date: Sun, 7 Dec 2025 13:08:08 +0530 Subject: [PATCH 5/9] fix: key id --- .../src-tauri/capabilities/mobile.json | 3 +- .../src/routes/(app)/scan-qr/scanLogic.ts | 20 +- .../signature-validator/package.json | 1 + .../signature-validator/src/index.ts | 30 +-- .../signature-validator/test-example.ts | 104 ---------- .../signature-validator/test-signature.ts | 78 ------- platforms/blabsy-w3ds-auth-api/package.json | 1 + .../src/controllers/AuthController.ts | 35 +++- .../src/components/login/login-main.tsx | 50 +++++ platforms/dreamSync/client/src/App.tsx | 2 + .../src/components/auth/login-screen.tsx | 50 +++++ platforms/dreamsync-api/package.json | 1 + .../src/controllers/AuthController.ts | 30 ++- platforms/eCurrency-api/package.json | 1 + .../src/controllers/AuthController.ts | 30 ++- platforms/eCurrency/client/src/App.tsx | 2 + .../src/components/auth/login-screen.tsx | 192 ++++++++++++++---- .../eCurrency/client/src/lib/apiClient.ts | 8 +- platforms/eReputation-api/package.json | 1 + .../src/controllers/AuthController.ts | 30 ++- platforms/eReputation/client/src/App.tsx | 2 + .../src/components/auth/login-screen.tsx | 47 +++++ .../eVoting/src/app/(auth)/login/page.tsx | 56 +++++ .../src/components/auth/login-screen.tsx | 50 +++++ platforms/evoting-api/package.json | 1 + .../src/controllers/AuthController.ts | 32 ++- .../group-charter-manager-api/package.json | 1 + .../src/controllers/AuthController.ts | 32 ++- .../src/components/auth/login-screen.tsx | 52 +++++ platforms/pictique-api/package.json | 1 + .../src/controllers/AuthController.ts | 32 ++- .../src/routes/(auth)/auth/+page.svelte | 46 ++++- pnpm-lock.yaml | 21 ++ 33 files changed, 767 insertions(+), 275 deletions(-) delete mode 100644 infrastructure/signature-validator/test-example.ts delete mode 100644 infrastructure/signature-validator/test-signature.ts diff --git a/infrastructure/eid-wallet/src-tauri/capabilities/mobile.json b/infrastructure/eid-wallet/src-tauri/capabilities/mobile.json index f1cd48af1..6e6c8369b 100644 --- a/infrastructure/eid-wallet/src-tauri/capabilities/mobile.json +++ b/infrastructure/eid-wallet/src-tauri/capabilities/mobile.json @@ -14,7 +14,8 @@ "deep-link:default", "crypto-hw:default", "notification:default", - "process:default" + "process:default", + "opener:allow-default-urls" ], "platforms": [ "iOS", diff --git a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts index 124aec059..009e7385c 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts +++ b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts @@ -7,6 +7,7 @@ import { requestPermissions, scan, } from "@tauri-apps/plugin-barcode-scanner"; +import { openUrl } from "@tauri-apps/plugin-opener"; import axios from "axios"; import { type Writable, get, writable } from "svelte/store"; @@ -272,13 +273,6 @@ export function createScanLogic({ sessionPayload, ); - const authPayload = { - ename: vault.ename, - session: get(session), - signature: signature, - appVersion: "0.4.0", - }; - const redirectUrl = get(redirect); if (!redirectUrl) { throw new Error( @@ -286,7 +280,17 @@ export function createScanLogic({ ); } - await axios.post(redirectUrl, authPayload); + // Strip path from redirectUri and append /deeplink-login + const loginUrl = new URL("/deeplink-login", redirectUrl); + loginUrl.searchParams.set("ename", vault.ename); + loginUrl.searchParams.set("session", get(session) as string); + loginUrl.searchParams.set("signature", signature); + loginUrl.searchParams.set("appVersion", "0.4.0"); + + console.log(`🔗 Opening login URL: ${loginUrl.toString()}`); + + // Open URL in browser using tauri opener + await openUrl(loginUrl.toString()); // Close the auth drawer first codeScannedDrawerOpen.set(false); diff --git a/infrastructure/signature-validator/package.json b/infrastructure/signature-validator/package.json index 402f33c15..620fb2c60 100644 --- a/infrastructure/signature-validator/package.json +++ b/infrastructure/signature-validator/package.json @@ -6,6 +6,7 @@ "types": "dist/index.d.ts", "scripts": { "build": "tsc", + "postinstall": "npm run build", "test": "vitest", "dev": "tsc --watch", "test:signature": "tsx test-example.ts", diff --git a/infrastructure/signature-validator/src/index.ts b/infrastructure/signature-validator/src/index.ts index 25504b9b1..952d7482c 100644 --- a/infrastructure/signature-validator/src/index.ts +++ b/infrastructure/signature-validator/src/index.ts @@ -112,7 +112,7 @@ async function decodeSignature(signature: string): Promise { * @param registryBaseUrl - Base URL of the registry service * @returns The public key in multibase format */ -async function getPublicKey(eName: string, registryBaseUrl: string): Promise { +async function getPublicKey(eName: string, registryBaseUrl: string): Promise { // Step 1: Resolve eVault URL from registry const resolveUrl = new URL(`/resolve?w3id=${encodeURIComponent(eName)}`, registryBaseUrl).toString(); const resolveResponse = await axios.get(resolveUrl, { @@ -136,7 +136,7 @@ async function getPublicKey(eName: string, registryBaseUrl: string): Promise