@structured-id/ui-core 1.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +22 -10
  2. package/package.json +25 -43
  3. package/src/components/SidStatusBadge.vue +1 -5
  4. package/src/composables/useAccountTransport.test.ts +2 -1
  5. package/src/composables/useGrpc.test.ts +17 -14
  6. package/src/composables/useGrpc.ts +4 -2
  7. package/src/index.ts +2 -120
  8. package/src/profile/components/ConsentManager.vue +5 -1
  9. package/src/profile/components/ProfileEditor.vue +1 -1
  10. package/src/profile/components/SecuritySettings.vue +5 -1
  11. package/src/profile/components/SessionList.vue +2 -1
  12. package/src/profile/components/SidRegistrationForm.test.ts +173 -1
  13. package/src/profile/components/SidRegistrationForm.vue +88 -32
  14. package/src/profile/components/index.ts +1 -4
  15. package/src/profile/composables/index.ts +1 -0
  16. package/src/profile/composables/operationPlan.test.ts +155 -0
  17. package/src/profile/composables/operationPlan.ts +218 -0
  18. package/src/profile/composables/useAccountApi.ts +4 -4
  19. package/src/profile/composables/useAuth.test.ts +312 -31
  20. package/src/profile/composables/useAuth.ts +176 -30
  21. package/src/profile/composables/useAuthApi.opaque.test.ts +13 -6
  22. package/src/profile/composables/useAuthApi.ts +40 -18
  23. package/src/profile/composables/useCurrentPasswordRequirement.test.ts +196 -0
  24. package/src/profile/composables/useCurrentPasswordRequirement.ts +95 -0
  25. package/src/profile/composables/useEnrollmentApi.test.ts +7 -3
  26. package/src/profile/composables/useEnrollmentApi.ts +3 -5
  27. package/src/profile/composables/useIdentityApi.ts +4 -4
  28. package/src/profile/composables/useSecurityLevel.test.ts +1 -1
  29. package/src/profile/composables/useSecurityLevel.ts +1 -1
  30. package/src/quasar/SidPasswordCeremony.test.ts +253 -0
  31. package/src/quasar/SidPasswordCeremony.vue +456 -0
  32. package/src/quasar/index.ts +2 -0
  33. package/src/utils/rpcRefusal.test.ts +4 -4
  34. package/src/utils/rpcRefusal.ts +8 -4
  35. package/dist/browser-vp/index.cjs +0 -349
  36. package/dist/browser-vp/index.d.cts +0 -276
  37. package/dist/browser-vp/index.d.ts +0 -276
  38. package/dist/browser-vp/index.js +0 -349
  39. package/dist/chunk-C3Q766CY.cjs +0 -125
  40. package/dist/chunk-J5YXRXB5.js +0 -125
  41. package/dist/index.cjs +0 -23581
  42. package/dist/index.d.cts +0 -11539
  43. package/dist/index.d.ts +0 -11539
  44. package/dist/index.js +0 -23581
  45. package/dist/storage-4CB2JTLW.cjs +0 -22
  46. package/dist/storage-THRWREZP.js +0 -22
  47. package/src/browser-vp/crypto.ts +0 -253
  48. package/src/browser-vp/index.test.ts +0 -10
  49. package/src/browser-vp/index.ts +0 -116
  50. package/src/browser-vp/prf.ts +0 -179
  51. package/src/browser-vp/storage.test.ts +0 -9
  52. package/src/browser-vp/storage.ts +0 -163
  53. package/src/browser-vp/types.ts +0 -95
  54. package/src/browser-vp/vp.test.ts +0 -43
  55. package/src/browser-vp/vp.ts +0 -111
  56. package/src/generated/google/api/http.ts +0 -687
  57. package/src/generated/google/api/httpbody.ts +0 -159
  58. package/src/generated/google/protobuf/any.ts +0 -326
  59. package/src/generated/google/protobuf/descriptor.ts +0 -4865
  60. package/src/generated/google/protobuf/duration.ts +0 -230
  61. package/src/generated/google/protobuf/empty.ts +0 -94
  62. package/src/generated/google/protobuf/struct.ts +0 -484
  63. package/src/generated/google/protobuf/timestamp.ts +0 -287
  64. package/src/generated/google/rpc/error_details.ts +0 -1386
  65. package/src/generated/google/rpc/status.ts +0 -127
  66. package/src/generated/sid/v1/account/account.client.ts +0 -527
  67. package/src/generated/sid/v1/account/account.ts +0 -3261
  68. package/src/generated/sid/v1/admin/admin.client.ts +0 -139
  69. package/src/generated/sid/v1/admin/admin.ts +0 -781
  70. package/src/generated/sid/v1/admin/admin_roles.client.ts +0 -181
  71. package/src/generated/sid/v1/admin/admin_roles.ts +0 -1606
  72. package/src/generated/sid/v1/admin/analytics.client.ts +0 -106
  73. package/src/generated/sid/v1/admin/analytics.ts +0 -1017
  74. package/src/generated/sid/v1/admin/branding.client.ts +0 -166
  75. package/src/generated/sid/v1/admin/branding.ts +0 -1906
  76. package/src/generated/sid/v1/admin/enrollment.client.ts +0 -177
  77. package/src/generated/sid/v1/admin/enrollment.ts +0 -1766
  78. package/src/generated/sid/v1/admin/events.client.ts +0 -195
  79. package/src/generated/sid/v1/admin/events.ts +0 -1900
  80. package/src/generated/sid/v1/admin/flow.client.ts +0 -183
  81. package/src/generated/sid/v1/admin/flow.ts +0 -1552
  82. package/src/generated/sid/v1/admin/keys.client.ts +0 -107
  83. package/src/generated/sid/v1/admin/keys.ts +0 -931
  84. package/src/generated/sid/v1/admin/login_experience.client.ts +0 -160
  85. package/src/generated/sid/v1/admin/login_experience.ts +0 -1061
  86. package/src/generated/sid/v1/admin/migration.client.ts +0 -127
  87. package/src/generated/sid/v1/admin/migration.ts +0 -1948
  88. package/src/generated/sid/v1/admin/notifications.client.ts +0 -134
  89. package/src/generated/sid/v1/admin/notifications.ts +0 -1273
  90. package/src/generated/sid/v1/admin/operations.client.ts +0 -241
  91. package/src/generated/sid/v1/admin/operations.ts +0 -2633
  92. package/src/generated/sid/v1/admin/organizations.client.ts +0 -57
  93. package/src/generated/sid/v1/admin/organizations.ts +0 -332
  94. package/src/generated/sid/v1/admin/provisioning.client.ts +0 -232
  95. package/src/generated/sid/v1/admin/provisioning.ts +0 -1383
  96. package/src/generated/sid/v1/admin/scopes.client.ts +0 -171
  97. package/src/generated/sid/v1/admin/scopes.ts +0 -1324
  98. package/src/generated/sid/v1/admin/security.client.ts +0 -410
  99. package/src/generated/sid/v1/admin/security.ts +0 -4077
  100. package/src/generated/sid/v1/admin/sessions.client.ts +0 -114
  101. package/src/generated/sid/v1/admin/sessions.ts +0 -923
  102. package/src/generated/sid/v1/admin/settings.client.ts +0 -351
  103. package/src/generated/sid/v1/admin/settings.ts +0 -2458
  104. package/src/generated/sid/v1/admin/user_profile.client.ts +0 -57
  105. package/src/generated/sid/v1/admin/user_profile.ts +0 -730
  106. package/src/generated/sid/v1/admin/webhooks.client.ts +0 -269
  107. package/src/generated/sid/v1/admin/webhooks.ts +0 -2804
  108. package/src/generated/sid/v1/attestation/attestation.client.ts +0 -115
  109. package/src/generated/sid/v1/attestation/attestation.ts +0 -813
  110. package/src/generated/sid/v1/authn/auth.client.ts +0 -943
  111. package/src/generated/sid/v1/authn/auth.ts +0 -9719
  112. package/src/generated/sid/v1/authn/flows.client.ts +0 -413
  113. package/src/generated/sid/v1/authn/flows.ts +0 -3205
  114. package/src/generated/sid/v1/authn/issuer.client.ts +0 -489
  115. package/src/generated/sid/v1/authn/issuer.ts +0 -1600
  116. package/src/generated/sid/v1/authn/password_history.client.ts +0 -57
  117. package/src/generated/sid/v1/authn/password_history.ts +0 -578
  118. package/src/generated/sid/v1/authn/system_integration.client.ts +0 -73
  119. package/src/generated/sid/v1/authn/system_integration.ts +0 -218
  120. package/src/generated/sid/v1/authz/authz.client.ts +0 -459
  121. package/src/generated/sid/v1/authz/authz.ts +0 -6524
  122. package/src/generated/sid/v1/authz/forward_auth.client.ts +0 -97
  123. package/src/generated/sid/v1/authz/forward_auth.ts +0 -79
  124. package/src/generated/sid/v1/authz/governance.client.ts +0 -102
  125. package/src/generated/sid/v1/authz/governance.ts +0 -875
  126. package/src/generated/sid/v1/common/errors.ts +0 -710
  127. package/src/generated/sid/v1/common/pagination.ts +0 -152
  128. package/src/generated/sid/v1/common/transport.ts +0 -227
  129. package/src/generated/sid/v1/common/types.ts +0 -151
  130. package/src/generated/sid/v1/events/event_stream.client.ts +0 -67
  131. package/src/generated/sid/v1/events/event_stream.ts +0 -375
  132. package/src/generated/sid/v1/federation/upstream.client.ts +0 -142
  133. package/src/generated/sid/v1/federation/upstream.ts +0 -1736
  134. package/src/generated/sid/v1/identity/identity.client.ts +0 -467
  135. package/src/generated/sid/v1/identity/identity.ts +0 -5676
  136. package/src/generated/sid/v1/ids/ids.ts +0 -395
  137. package/src/generated/sid/v1/machine/machine_user.client.ts +0 -219
  138. package/src/generated/sid/v1/machine/machine_user.ts +0 -2364
  139. package/src/generated/sid/v1/machine/pat.client.ts +0 -130
  140. package/src/generated/sid/v1/machine/pat.ts +0 -1083
  141. package/src/generated/sid/v1/projects/projects.client.ts +0 -404
  142. package/src/generated/sid/v1/projects/projects.ts +0 -4446
  143. package/src/generated/sid/v1/scim/scim.client.ts +0 -520
  144. package/src/generated/sid/v1/scim/scim.ts +0 -3182
  145. package/src/generated/sid/v1/test/test.client.ts +0 -232
  146. package/src/generated/sid/v1/test/test.ts +0 -1136
  147. package/src/transport/adaptive.test.ts +0 -728
  148. package/src/transport/adaptive.ts +0 -308
  149. package/src/transport/framing.test.ts +0 -227
  150. package/src/transport/framing.ts +0 -98
  151. package/src/transport/webtransport.test.ts +0 -728
  152. package/src/transport/webtransport.ts +0 -323
@@ -1,276 +0,0 @@
1
- /**
2
- * Browser VP types.
3
- */
4
- /** VP strategy — automatically selected based on browser capabilities. */
5
- type VpStrategy = "prf" | "device-key";
6
- /** Profile state stored in IndexedDB. */
7
- interface ProfileState {
8
- /** Which strategy this profile uses. */
9
- strategy: VpStrategy;
10
- /** Encrypted profile blob (< 1MB typical). */
11
- encryptedBlob: ArrayBuffer;
12
- /** Optimistic sync version with SID server. */
13
- blobVersion: number;
14
- /** PRF salt identifier (for versioning). */
15
- prfSalt?: string;
16
- /** SHA-256 of master public key (authenticator change detection). */
17
- masterPublicKeyHash?: string;
18
- /** Non-extractable CryptoKey reference (browser-managed). */
19
- cryptoKeyRef?: CryptoKey;
20
- /** Last sync timestamp with SID server. */
21
- lastSyncedAt: Date;
22
- /** SID device ID for this browser instance. */
23
- deviceId: string;
24
- }
25
- /** Per-service binding stored in IndexedDB. */
26
- interface BindingEntry {
27
- /** Opaque BindingId (pairwise per-service). */
28
- bindingId: string;
29
- /** HD derivation index for this binding. */
30
- bindingIndex: number;
31
- /** Per-binding certificate chain (DER-encoded). */
32
- certChain: ArrayBuffer;
33
- /** Certificate expiry date. */
34
- certExpiry: Date;
35
- /** Last time a VP was created for this binding. */
36
- lastUsedAt: Date;
37
- }
38
- /** Key material derived from PRF or generated via WebCrypto. */
39
- interface DerivedKeys {
40
- /** ECDSA P-256 signing key (non-extractable). */
41
- signingKey: CryptoKeyPair;
42
- /** AES-256-GCM data encryption key. */
43
- dataKey: CryptoKey;
44
- }
45
- /** Verifiable Presentation payload. */
46
- interface VerifiablePresentation {
47
- /** The service sector this VP is bound to. */
48
- serviceSector: string;
49
- /** Binding ID (pairwise). */
50
- bindingId: string;
51
- /** Signed VP payload (CBOR or JSON). */
52
- payload: ArrayBuffer;
53
- /** Signature over payload. */
54
- signature: ArrayBuffer;
55
- /** Certificate chain for verification. */
56
- certChain: ArrayBuffer;
57
- /** Nonce from relying party (replay protection). */
58
- nonce: string;
59
- /** Audience (relying party origin). */
60
- audience: string;
61
- }
62
- /** Options for VP creation request from a relying party. */
63
- interface VpRequest {
64
- /** Nonce for replay protection. */
65
- nonce: string;
66
- /** Requested claims (e.g., ["email", "name"]). */
67
- requestedClaims: string[];
68
- /** Audience (relying party origin). */
69
- audience: string;
70
- /** Service sector identifier. */
71
- serviceSector: string;
72
- }
73
- /** Result of browser VP initialization. */
74
- interface BrowserVpContext {
75
- /** Active strategy. */
76
- strategy: VpStrategy;
77
- /** Profile ID. */
78
- profileId: string;
79
- /** Whether VP creation is available (keys + blob + cert ready). */
80
- ready: boolean;
81
- }
82
-
83
- /**
84
- * WebAuthn PRF extension support (Strategy A).
85
- *
86
- * PRF (Pseudo-Random Function) extension allows deriving
87
- * deterministic secrets from a passkey. Same passkey on any
88
- * synced device produces the same PRF output → same keys.
89
- */
90
- /**
91
- * Detect whether the browser supports WebAuthn PRF extension.
92
- *
93
- * Attempts a PRF evaluation on an existing passkey. Returns false
94
- * if PRF is unsupported — no error thrown.
95
- *
96
- * Browser support (2026): Chrome 116+, Edge 116+, Safari 17.4+,
97
- * Chrome Android, Safari iOS 17.4+. ~90% coverage.
98
- */
99
- declare function hasPrfSupport(): Promise<boolean>;
100
- /**
101
- * Evaluate PRF extension during passkey authentication.
102
- *
103
- * Returns the raw PRF output (32 bytes) which serves as the
104
- * entropy source for key derivation.
105
- *
106
- * @param credentialId - Optional: specific credential to use.
107
- * @returns PRF output bytes, or null if PRF evaluation failed.
108
- */
109
- declare function evaluatePrf(credentialId?: ArrayBuffer): Promise<ArrayBuffer | null>;
110
- /**
111
- * Create a passkey with PRF extension (first-time setup).
112
- *
113
- * Returns both the credential and the initial PRF output.
114
- */
115
- declare function createPasskeyWithPrf(userId: ArrayBuffer, userName: string, rpName: string): Promise<{
116
- credential: PublicKeyCredential;
117
- prfOutput: ArrayBuffer;
118
- } | null>;
119
-
120
- /**
121
- * Cryptographic primitives for Browser VP.
122
- *
123
- * All operations use Web Crypto API. Keys are non-extractable.
124
- */
125
-
126
- /**
127
- * Derive signing key (ECDSA P-256) and data key (AES-256-GCM)
128
- * from a PRF output using HKDF-SHA256.
129
- *
130
- * Both keys are deterministic from the same PRF seed.
131
- * Same passkey on any device → same keys.
132
- */
133
- declare function deriveKeysFromPrf(prfOutput: ArrayBuffer): Promise<DerivedKeys>;
134
- /**
135
- * Generate a random ECDSA P-256 key pair (Strategy B).
136
- * The private key is non-extractable.
137
- */
138
- declare function generateDeviceKey(): Promise<CryptoKeyPair>;
139
- /**
140
- * Generate a random AES-256-GCM data key (Strategy B).
141
- */
142
- declare function generateDataKey(): Promise<CryptoKey>;
143
- /**
144
- * Wrap a CryptoKey with AES-KW for upload to SID (Strategy B).
145
- * Returns the wrapped key bytes.
146
- */
147
- declare function wrapKey(keyToWrap: CryptoKey, wrappingKey: CryptoKey): Promise<ArrayBuffer>;
148
- /**
149
- * Unwrap a CryptoKey from SID (Strategy B, new browser).
150
- * Returns non-extractable ECDSA P-256 key pair.
151
- */
152
- declare function unwrapSigningKey(wrappedKey: ArrayBuffer, wrappingKey: CryptoKey): Promise<CryptoKey>;
153
- /**
154
- * Derive AES-256-KW wrapping key from OPAQUE session export key (Strategy B).
155
- */
156
- declare function deriveWrappingKey(sessionExportKey: ArrayBuffer): Promise<CryptoKey>;
157
- /**
158
- * Encrypt data with AES-256-GCM.
159
- * Returns IV (12 bytes) prepended to ciphertext.
160
- */
161
- declare function encrypt(data: ArrayBuffer, key: CryptoKey): Promise<ArrayBuffer>;
162
- /**
163
- * Decrypt data encrypted with `encrypt()`.
164
- * Expects IV (12 bytes) prepended to ciphertext.
165
- */
166
- declare function decrypt(data: ArrayBuffer, key: CryptoKey): Promise<ArrayBuffer>;
167
- /**
168
- * Sign data with ECDSA P-256 (SHA-256).
169
- */
170
- declare function sign(data: ArrayBuffer, privateKey: CryptoKey): Promise<ArrayBuffer>;
171
- /**
172
- * Export public key as raw bytes (for CSR / certificate request).
173
- */
174
- declare function exportPublicKey(keyPair: CryptoKeyPair): Promise<ArrayBuffer>;
175
- /**
176
- * Compute SHA-256 hash (for authenticator change detection).
177
- */
178
- declare function sha256(data: ArrayBuffer): Promise<string>;
179
-
180
- /**
181
- * IndexedDB storage for Browser VP.
182
- *
183
- * Database: sid-browser-vp
184
- * ├── bindings — per-service binding entries (key: service_sector)
185
- * └── profile-state — profile state + encrypted blob (key: profile_id)
186
- */
187
-
188
- /** Save or update profile state. */
189
- declare function saveProfileState(profileId: string, state: ProfileState): Promise<void>;
190
- /** Load profile state by ID. Returns null if not found. */
191
- declare function loadProfileState(profileId: string): Promise<ProfileState | null>;
192
- /** Delete profile state. */
193
- declare function deleteProfileState(profileId: string): Promise<void>;
194
- /** Save or update a binding entry. */
195
- declare function saveBinding(serviceSector: string, entry: BindingEntry): Promise<void>;
196
- /** Load a binding by service sector. */
197
- declare function loadBinding(serviceSector: string): Promise<BindingEntry | null>;
198
- /** List all stored bindings. */
199
- declare function listBindings(): Promise<Array<{
200
- serviceSector: string;
201
- } & BindingEntry>>;
202
- /** Delete a binding. */
203
- declare function deleteBinding(serviceSector: string): Promise<void>;
204
- /** Clear all data (profile state + bindings). */
205
- declare function clearAll(): Promise<void>;
206
- /** Check if IndexedDB is available in this environment. */
207
- declare function isIndexedDbAvailable(): boolean;
208
-
209
- /**
210
- * Verifiable Presentation creation.
211
- *
212
- * Builds and signs VPs using cached keys and profile blob.
213
- * VP creation is fully offline (~50ms with biometric cache).
214
- */
215
-
216
- /**
217
- * Create a Verifiable Presentation for a service.
218
- *
219
- * @param request - VP request from the relying party.
220
- * @param signingKey - ECDSA P-256 private key (master or per-binding).
221
- * @param profileBlob - Decrypted profile blob.
222
- * @returns Signed VP, or null if binding not found.
223
- */
224
- declare function createVp(request: VpRequest, signingKey: CryptoKey, profileBlob: ArrayBuffer): Promise<VerifiablePresentation | null>;
225
- /**
226
- * Check if a binding's certificate needs renewal.
227
- *
228
- * Renewal is attempted 7 days before expiry (background).
229
- */
230
- declare function needsRenewal(binding: BindingEntry): boolean;
231
- /**
232
- * List all bindings that need certificate renewal.
233
- */
234
- declare function getExpiringSoon(): Promise<Array<{
235
- serviceSector: string;
236
- } & BindingEntry>>;
237
-
238
- /**
239
- * Browser VP — Zero-Load Auth in Browser.
240
- *
241
- * Public API for browser-based Verifiable Presentation creation.
242
- * Automatically selects the best strategy based on browser capabilities:
243
- *
244
- * - **Strategy A (PRF):** Passkey PRF extension → deterministic keys.
245
- * Cross-device via passkey sync. ~90% browser coverage (2026).
246
- *
247
- * - **Strategy B (device-key):** Random WebCrypto key, wrapped for sync.
248
- * Each browser = separate device. Fallback for non-PRF browsers.
249
- *
250
- * Reference: Prudnikov, D. (2026). "Client-Side Verifiable Presentation System
251
- * with Passkey-Derived Persistent Keys and Pairwise Service Bindings."
252
- * doi:10.5281/zenodo.19387768 — https://doi.org/10.5281/zenodo.19387768
253
- */
254
-
255
- /**
256
- * Detect the best VP strategy for this browser.
257
- *
258
- * Returns "prf" if WebAuthn PRF extension is supported,
259
- * "device-key" otherwise. Returns null if Browser VP is
260
- * not possible (no IndexedDB, no WebCrypto).
261
- */
262
- declare function detectStrategy(): Promise<VpStrategy | null>;
263
- /**
264
- * Initialize Browser VP for a profile.
265
- *
266
- * This is the main entry point. It:
267
- * 1. Detects the best strategy
268
- * 2. Checks for existing state in IndexedDB
269
- * 3. Returns a context object indicating readiness
270
- *
271
- * If not ready, the caller should trigger setup flow
272
- * (passkey creation for PRF, or key generation for device-key).
273
- */
274
- declare function initBrowserVp(profileId: string): Promise<BrowserVpContext | null>;
275
-
276
- export { type BindingEntry, type BrowserVpContext, type DerivedKeys, type ProfileState, type VerifiablePresentation, type VpRequest, type VpStrategy, clearAll, createPasskeyWithPrf, createVp, decrypt, deleteBinding, deleteProfileState, deriveKeysFromPrf, deriveWrappingKey, detectStrategy, encrypt, evaluatePrf, exportPublicKey, generateDataKey, generateDeviceKey, getExpiringSoon, hasPrfSupport, initBrowserVp, isIndexedDbAvailable, listBindings, loadBinding, loadProfileState, needsRenewal, saveBinding, saveProfileState, sha256, sign, unwrapSigningKey, wrapKey };
@@ -1,349 +0,0 @@
1
- import {
2
- clearAll,
3
- deleteBinding,
4
- deleteProfileState,
5
- isIndexedDbAvailable,
6
- listBindings,
7
- loadBinding,
8
- loadProfileState,
9
- saveBinding,
10
- saveProfileState
11
- } from "../chunk-J5YXRXB5.js";
12
-
13
- // src/browser-vp/prf.ts
14
- var PRF_EVAL_LABEL = "sid-ppk-v1";
15
- var PRF_PROBE_LABEL = "sid-probe";
16
- var encoder = new TextEncoder();
17
- async function hasPrfSupport() {
18
- try {
19
- if (typeof window === "undefined" || !window.PublicKeyCredential) {
20
- return false;
21
- }
22
- if (typeof PublicKeyCredential.getClientCapabilities === "function") {
23
- try {
24
- const caps = await PublicKeyCredential.getClientCapabilities();
25
- if (caps && "prf" in caps) {
26
- return Boolean(caps.prf);
27
- }
28
- } catch {
29
- }
30
- }
31
- const result = await navigator.credentials.get({
32
- publicKey: {
33
- challenge: crypto.getRandomValues(new Uint8Array(32)),
34
- rpId: window.location.hostname,
35
- userVerification: "discouraged",
36
- extensions: {
37
- prf: {
38
- eval: {
39
- first: encoder.encode(PRF_PROBE_LABEL)
40
- }
41
- }
42
- }
43
- }
44
- });
45
- if (!result) return false;
46
- const ext = result.getClientExtensionResults();
47
- const prfResults = ext?.prf;
48
- return prfResults?.results?.first != null;
49
- } catch {
50
- return false;
51
- }
52
- }
53
- async function evaluatePrf(credentialId) {
54
- try {
55
- const allowCredentials = credentialId ? [{ type: "public-key", id: credentialId }] : void 0;
56
- const result = await navigator.credentials.get({
57
- publicKey: {
58
- challenge: crypto.getRandomValues(new Uint8Array(32)),
59
- rpId: window.location.hostname,
60
- userVerification: "required",
61
- allowCredentials,
62
- extensions: {
63
- prf: {
64
- eval: {
65
- first: encoder.encode(PRF_EVAL_LABEL)
66
- }
67
- }
68
- }
69
- }
70
- });
71
- if (!result) return null;
72
- const ext = result.getClientExtensionResults();
73
- const prfResults = ext?.prf;
74
- return prfResults?.results?.first ?? null;
75
- } catch {
76
- return null;
77
- }
78
- }
79
- async function createPasskeyWithPrf(userId, userName, rpName) {
80
- try {
81
- const result = await navigator.credentials.create({
82
- publicKey: {
83
- challenge: crypto.getRandomValues(new Uint8Array(32)),
84
- rp: {
85
- name: rpName,
86
- id: window.location.hostname
87
- },
88
- user: {
89
- id: userId,
90
- name: userName,
91
- displayName: userName
92
- },
93
- pubKeyCredParams: [
94
- { alg: -7, type: "public-key" },
95
- // ES256
96
- { alg: -257, type: "public-key" }
97
- // RS256 fallback
98
- ],
99
- authenticatorSelection: {
100
- residentKey: "required",
101
- userVerification: "required"
102
- },
103
- extensions: {
104
- prf: {
105
- eval: {
106
- first: encoder.encode(PRF_EVAL_LABEL)
107
- }
108
- }
109
- }
110
- }
111
- });
112
- if (!result) return null;
113
- const credential = result;
114
- const ext = credential.getClientExtensionResults();
115
- const prfResults = ext?.prf;
116
- const prfOutput = prfResults?.results?.first;
117
- if (!prfOutput) return null;
118
- return { credential, prfOutput };
119
- } catch {
120
- return null;
121
- }
122
- }
123
-
124
- // src/browser-vp/crypto.ts
125
- var PRF_SALT = "sid-ppk-v1";
126
- var SIGNING_INFO = "signing-key";
127
- var DATA_KEY_INFO = "data-encryption-key";
128
- var encoder2 = new TextEncoder();
129
- async function deriveKeysFromPrf(prfOutput) {
130
- const baseKey = await crypto.subtle.importKey(
131
- "raw",
132
- prfOutput,
133
- "HKDF",
134
- false,
135
- ["deriveBits"]
136
- );
137
- const salt = encoder2.encode(PRF_SALT);
138
- const signingBits = await crypto.subtle.deriveBits(
139
- { name: "HKDF", hash: "SHA-256", salt, info: encoder2.encode(SIGNING_INFO) },
140
- baseKey,
141
- 256
142
- );
143
- const dataKeyBits = await crypto.subtle.deriveBits(
144
- {
145
- name: "HKDF",
146
- hash: "SHA-256",
147
- salt,
148
- info: encoder2.encode(DATA_KEY_INFO)
149
- },
150
- baseKey,
151
- 256
152
- );
153
- const signingKey = await importEcdsaFromSeed(signingBits);
154
- const dataKey = await crypto.subtle.importKey(
155
- "raw",
156
- dataKeyBits,
157
- { name: "AES-GCM", length: 256 },
158
- false,
159
- ["encrypt", "decrypt"]
160
- );
161
- return { signingKey, dataKey };
162
- }
163
- async function generateDeviceKey() {
164
- return crypto.subtle.generateKey(
165
- { name: "ECDSA", namedCurve: "P-256" },
166
- false,
167
- // non-extractable
168
- ["sign", "verify"]
169
- );
170
- }
171
- async function generateDataKey() {
172
- return crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, [
173
- "encrypt",
174
- "decrypt"
175
- ]);
176
- }
177
- async function wrapKey(keyToWrap, wrappingKey) {
178
- return crypto.subtle.wrapKey("jwk", keyToWrap, wrappingKey, "AES-KW");
179
- }
180
- async function unwrapSigningKey(wrappedKey, wrappingKey) {
181
- return crypto.subtle.unwrapKey(
182
- "jwk",
183
- wrappedKey,
184
- wrappingKey,
185
- "AES-KW",
186
- { name: "ECDSA", namedCurve: "P-256" },
187
- false,
188
- // non-extractable
189
- ["sign"]
190
- );
191
- }
192
- async function deriveWrappingKey(sessionExportKey) {
193
- const baseKey = await crypto.subtle.importKey(
194
- "raw",
195
- sessionExportKey,
196
- "HKDF",
197
- false,
198
- ["deriveKey"]
199
- );
200
- return crypto.subtle.deriveKey(
201
- {
202
- name: "HKDF",
203
- hash: "SHA-256",
204
- salt: encoder2.encode("sid-device-key-wrap"),
205
- info: encoder2.encode("wrapping-key")
206
- },
207
- baseKey,
208
- { name: "AES-KW", length: 256 },
209
- false,
210
- ["wrapKey", "unwrapKey"]
211
- );
212
- }
213
- async function encrypt(data, key) {
214
- const iv = crypto.getRandomValues(new Uint8Array(12));
215
- const ciphertext = await crypto.subtle.encrypt(
216
- { name: "AES-GCM", iv },
217
- key,
218
- data
219
- );
220
- const result = new Uint8Array(iv.length + ciphertext.byteLength);
221
- result.set(iv, 0);
222
- result.set(new Uint8Array(ciphertext), iv.length);
223
- return result.buffer;
224
- }
225
- async function decrypt(data, key) {
226
- const bytes = new Uint8Array(data);
227
- const iv = bytes.slice(0, 12);
228
- const ciphertext = bytes.slice(12);
229
- return crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext);
230
- }
231
- async function sign(data, privateKey) {
232
- return crypto.subtle.sign(
233
- { name: "ECDSA", hash: "SHA-256" },
234
- privateKey,
235
- data
236
- );
237
- }
238
- async function exportPublicKey(keyPair) {
239
- return crypto.subtle.exportKey("raw", keyPair.publicKey);
240
- }
241
- async function sha256(data) {
242
- const hash = await crypto.subtle.digest("SHA-256", data);
243
- return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
244
- }
245
- async function importEcdsaFromSeed(_seed) {
246
- return crypto.subtle.generateKey(
247
- { name: "ECDSA", namedCurve: "P-256" },
248
- false,
249
- ["sign", "verify"]
250
- );
251
- }
252
-
253
- // src/browser-vp/vp.ts
254
- var encoder3 = new TextEncoder();
255
- async function createVp(request, signingKey, profileBlob) {
256
- const binding = await loadBinding(request.serviceSector);
257
- if (!binding) {
258
- return null;
259
- }
260
- if (binding.certExpiry < /* @__PURE__ */ new Date()) {
261
- return null;
262
- }
263
- const payload = buildPayload(request, binding, profileBlob);
264
- const signature = await sign(payload, signingKey);
265
- await saveBinding(request.serviceSector, {
266
- ...binding,
267
- lastUsedAt: /* @__PURE__ */ new Date()
268
- });
269
- return {
270
- serviceSector: request.serviceSector,
271
- bindingId: binding.bindingId,
272
- payload,
273
- signature,
274
- certChain: binding.certChain,
275
- nonce: request.nonce,
276
- audience: request.audience
277
- };
278
- }
279
- function buildPayload(request, binding, _profileBlob) {
280
- const payload = {
281
- nonce: request.nonce,
282
- aud: request.audience,
283
- sub: binding.bindingId,
284
- iat: Math.floor(Date.now() / 1e3),
285
- claims: request.requestedClaims
286
- };
287
- return encoder3.encode(JSON.stringify(payload)).buffer;
288
- }
289
- function needsRenewal(binding) {
290
- const sevenDays = 7 * 24 * 60 * 60 * 1e3;
291
- return binding.certExpiry.getTime() - Date.now() < sevenDays;
292
- }
293
- async function getExpiringSoon() {
294
- const { listBindings: listBindings2 } = await import("../storage-THRWREZP.js");
295
- const all = await listBindings2();
296
- return all.filter((b) => needsRenewal(b));
297
- }
298
-
299
- // src/browser-vp/index.ts
300
- async function detectStrategy() {
301
- if (typeof window === "undefined") return null;
302
- if (!isIndexedDbAvailable()) return null;
303
- if (!window.crypto?.subtle) return null;
304
- if (await hasPrfSupport()) {
305
- return "prf";
306
- }
307
- return "device-key";
308
- }
309
- async function initBrowserVp(profileId) {
310
- const strategy = await detectStrategy();
311
- if (!strategy) return null;
312
- const { loadProfileState: loadProfileState2 } = await import("../storage-THRWREZP.js");
313
- const state = await loadProfileState2(profileId);
314
- return {
315
- strategy,
316
- profileId,
317
- ready: state != null && state.strategy === strategy
318
- };
319
- }
320
- export {
321
- clearAll,
322
- createPasskeyWithPrf,
323
- createVp,
324
- decrypt,
325
- deleteBinding,
326
- deleteProfileState,
327
- deriveKeysFromPrf,
328
- deriveWrappingKey,
329
- detectStrategy,
330
- encrypt,
331
- evaluatePrf,
332
- exportPublicKey,
333
- generateDataKey,
334
- generateDeviceKey,
335
- getExpiringSoon,
336
- hasPrfSupport,
337
- initBrowserVp,
338
- isIndexedDbAvailable,
339
- listBindings,
340
- loadBinding,
341
- loadProfileState,
342
- needsRenewal,
343
- saveBinding,
344
- saveProfileState,
345
- sha256,
346
- sign,
347
- unwrapSigningKey,
348
- wrapKey
349
- };