Skip to content

Commit f0c2d4e

Browse files
committed
feat: use wallet-sdk in eID Wallet (#799)
* refactor: move eID wallet to wallet SDK * docs: wallet sdk docs * refactor: remove dead code * fix: tests * fix: dev sandbox build * chore: format * chore: fix check # Conflicts: # infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte # infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte
1 parent 041341b commit f0c2d4e

File tree

29 files changed

+1281
-998
lines changed

29 files changed

+1281
-998
lines changed

docs/docs/Infrastructure/eID-Wallet.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,8 @@ The wallet creates signatures for various purposes:
292292

293293
- **Framework**: Tauri (Rust + Web frontend)
294294
- **Frontend**: SvelteKit + TypeScript
295-
- **Key APIs**:
295+
- **wallet-sdk**: The wallet uses the [wallet-sdk](/docs/Infrastructure/wallet-sdk) package for provisioning, platform authentication (signing), and public-key sync to eVault. Crypto is provided by the existing KeyService via a **CryptoAdapter** (BYOC), so hardware/software key manager behavior is unchanged.
296+
- **Key APIs**:
296297
- iOS: LocalAuthentication (Secure Enclave)
297298
- Android: KeyStore (HSM)
298299
- Web: Web Crypto API
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
---
2+
sidebar_position: 6
3+
---
4+
5+
# wallet-sdk
6+
7+
The **wallet-sdk** is a small TypeScript package that implements the high-level flows for eVault provisioning, platform authentication (signing), and public-key sync. It is **crypto-agnostic**: you supply a **CryptoAdapter** (BYOC – bring your own crypto), and the SDK handles the HTTP and protocol steps.
8+
9+
The [eID Wallet](/docs/Infrastructure/eID-Wallet) uses wallet-sdk with an adapter that delegates to its KeyService, so hardware/software key manager behavior is unchanged.
10+
11+
## Overview
12+
13+
- **Package**: `wallet-sdk` (workspace package under `packages/wallet-sdk/`)
14+
- **Exports**: `CryptoAdapter` type, `provision`, `authenticate`, `syncPublicKeyToEvault`, and their option/result types
15+
- **Dependencies**: `jose` (for JWT verification when checking existing keys during sync). Uses the global `fetch` for HTTP
16+
17+
## CryptoAdapter (BYOC)
18+
19+
Implement this interface to plug in your key storage (e.g. KeyService + hardware/software managers):
20+
21+
```typescript
22+
interface CryptoAdapter {
23+
getPublicKey(keyId: string, context: string): Promise<string | undefined>;
24+
signPayload(keyId: string, context: string, payload: string): Promise<string>;
25+
ensureKey(keyId: string, context: string): Promise<{ created: boolean }>;
26+
}
27+
```
28+
29+
- **getPublicKey**: Return the public key for the given key id and context, or `undefined` if the key does not exist.
30+
- **signPayload**: Sign the payload with the same key as used for `getPublicKey`. Return the signature string (encoding is up to the adapter, e.g. base64 or multibase).
31+
- **ensureKey**: Ensure a key exists for the given key id and context; create it if needed. Return `{ created: true }` if a new key was created, `{ created: false }` otherwise.
32+
33+
Contexts used by the eID Wallet include `"onboarding"`, `"pre-verification"`, and `"signing"`. The SDK does not interpret contexts; it only passes them through to the adapter.
34+
35+
## API
36+
37+
### provision(adapter, options)
38+
39+
Provisions an eVault: fetches entropy from the Registry, gets the public key from the adapter, and POSTs to the Provisioner.
40+
41+
**Flow**:
42+
43+
1. `GET {registryUrl}/entropy` → obtain entropy token
44+
2. `adapter.getPublicKey(keyId, context)` → public key (must exist; ensure key before calling if needed)
45+
3. `POST {provisionerUrl}/provision` with `{ registryEntropy, namespace, verificationId, publicKey }`
46+
47+
**Options**: `registryUrl`, `provisionerUrl`, `namespace`, `verificationId`, and optionally `keyId` (default `"default"`), `context` (default derived from `isPreVerification`), `isPreVerification`.
48+
49+
**Returns**: `{ success, w3id, uri }`. Throws on HTTP or validation errors.
50+
51+
**Example** (eID Wallet pre-verification):
52+
53+
```typescript
54+
const result = await provision(globalState.walletSdkAdapter, {
55+
registryUrl: PUBLIC_REGISTRY_URL,
56+
provisionerUrl: PUBLIC_PROVISIONER_URL,
57+
namespace: uuidv4(),
58+
verificationId,
59+
keyId: "default",
60+
context: "pre-verification",
61+
isPreVerification: true,
62+
});
63+
// result.uri, result.w3id
64+
```
65+
66+
### authenticate(adapter, options)
67+
68+
Ensures the key exists and signs the session payload. The **caller** is responsible for sending the signature to the platform (e.g. POST to redirect URL or open deeplink).
69+
70+
**Flow**:
71+
72+
1. `adapter.ensureKey(keyId, context)`
73+
2. `adapter.signPayload(keyId, context, sessionId)`
74+
3. Return `{ signature }`
75+
76+
**Options**: `sessionId`, `context`, and optionally `keyId` (default `"default"`).
77+
78+
**Returns**: `{ signature }`.
79+
80+
**Example** (eID Wallet auth):
81+
82+
```typescript
83+
const { signature } = await authenticate(globalState.walletSdkAdapter, {
84+
sessionId: sessionPayload,
85+
keyId: "default",
86+
context: isFake ? "pre-verification" : "onboarding",
87+
});
88+
// Then POST to redirect URL: { ename, session, signature, appVersion }
89+
```
90+
91+
### syncPublicKeyToEvault(adapter, options)
92+
93+
Syncs the adapter’s public key to the eVault: calls `/whois`, optionally skips PATCH if the current key is already present in key-binding certificates (using Registry JWKS), then `PATCH /public-key` if needed.
94+
95+
**Flow**:
96+
97+
1. `GET {evaultUri}/whois` with header `X-ENAME: {eName}`
98+
2. If `registryUrl` is provided and whois returns key-binding certificates, verify with Registry’s `/.well-known/jwks.json` and skip PATCH if the current public key is already in a valid cert for this eName
99+
3. `adapter.getPublicKey(keyId, context)`
100+
4. `PATCH {evaultUri}/public-key` with `{ publicKey }`, headers `X-ENAME` and optional `Authorization: Bearer {authToken}`
101+
102+
The SDK does not read or write `localStorage`; the caller can set a hint (e.g. `publicKeySaved_${eName}`) after a successful sync.
103+
104+
**Options**: `evaultUri`, `eName`, `context`, and optionally `keyId` (default `"default"`), `authToken`, `registryUrl` (for skip-if-present verification).
105+
106+
**Example** (eID Wallet):
107+
108+
```typescript
109+
await syncPublicKeyToEvault(globalState.walletSdkAdapter, {
110+
evaultUri: vault.uri,
111+
eName,
112+
keyId: "default",
113+
context: isFake ? "pre-verification" : "onboarding",
114+
authToken: PUBLIC_EID_WALLET_TOKEN || null,
115+
registryUrl: PUBLIC_REGISTRY_URL,
116+
});
117+
```
118+
119+
## Use in the eID Wallet
120+
121+
The eID Wallet:
122+
123+
1. Implements a **CryptoAdapter** by wrapping KeyService in `createKeyServiceCryptoAdapter(keyService)` (see `src/lib/wallet-sdk-adapter.ts`).
124+
2. Exposes this adapter on GlobalState as `walletSdkAdapter` and passes it into VaultController.
125+
3. Uses **provision** in the onboarding (pre-verification) and verify (real user) flows instead of inline entropy + provision calls.
126+
4. Uses **authenticate** in the scan-qr auth and signing flows, then performs the POST or deeplink open in the UI.
127+
5. Uses **syncPublicKeyToEvault** inside `VaultController.syncPublicKey(eName)` instead of inline whois + PATCH logic.
128+
129+
See [eID Wallet](/docs/Infrastructure/eID-Wallet) for architecture and key manager details.
130+
131+
## References
132+
133+
- [eID Wallet](/docs/Infrastructure/eID-Wallet) – Consumer of wallet-sdk; KeyService and CryptoAdapter
134+
- [Registry](/docs/Infrastructure/Registry) – Entropy and key-binding certificates
135+
- [eVault](/docs/Infrastructure/eVault) – Whois and public-key storage
136+
- [Links](/docs/W3DS%20Basics/Links) – Production URLs (Provisioner, Registry)

infrastructure/dev-sandbox/src/routes/+page.svelte

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
<script lang="ts">
2-
import { provision, syncPublicKeyToEvault, signPayload } from "wallet-sdk";
2+
import {
3+
provision,
4+
syncPublicKeyToEvaultWithOptions,
5+
signPayload,
6+
} from "wallet-sdk";
37
import { env } from "$env/dynamic/public";
48
import { PersistingWebCryptoAdapter } from "$lib/PersistingWebCryptoAdapter";
59
import {
@@ -204,11 +208,12 @@
204208
addLog("info", "Syncing public key…");
205209
try {
206210
const token = await ensurePlatformToken(identity);
207-
await syncPublicKeyToEvault({
211+
await syncPublicKeyToEvaultWithOptions({
208212
evaultUrl: identity.uri,
209213
eName: identity.w3id,
210214
cryptoAdapter: adapter,
211215
keyId: identity.keyId,
216+
context: "onboarding",
212217
token,
213218
});
214219
addLog("success", "Public key synced");
@@ -296,6 +301,7 @@
296301
const signature = await signPayload({
297302
cryptoAdapter: adapter,
298303
keyId: selectedIdentity.keyId,
304+
context: "onboarding",
299305
payload: sessionId,
300306
});
301307
const body = {
@@ -339,6 +345,7 @@
339345
const signature = await signPayload({
340346
cryptoAdapter: adapter,
341347
keyId: selectedIdentity.keyId,
348+
context: "onboarding",
342349
payload: sessionId,
343350
});
344351
const body = {
@@ -404,6 +411,7 @@
404411
const sig = await signPayload({
405412
cryptoAdapter: adapter,
406413
keyId: selectedIdentity.keyId,
414+
context: "onboarding",
407415
payload: signPayloadInput,
408416
});
409417
signResult = sig;

infrastructure/eid-wallet/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
"svelte-loading-spinners": "^0.3.6",
4747
"svelte-qrcode": "^1.0.1",
4848
"tailwind-merge": "^3.0.2",
49-
"uuid": "^11.1.0"
49+
"uuid": "^11.1.0",
50+
"wallet-sdk": "workspace:*"
5051
},
5152
"devDependencies": {
5253
"@biomejs/biome": "^1.9.4",

0 commit comments

Comments
 (0)