@interop/wallet-core 0.23.1 → 0.25.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 (125) hide show
  1. package/README.md +41 -22
  2. package/dist/clients/index.d.ts +6 -4
  3. package/dist/clients/index.d.ts.map +1 -1
  4. package/dist/clients/index.js +5 -3
  5. package/dist/clients/index.js.map +1 -1
  6. package/dist/clients/policy.d.ts +11 -3
  7. package/dist/clients/policy.d.ts.map +1 -1
  8. package/dist/clients/policy.js +12 -4
  9. package/dist/clients/policy.js.map +1 -1
  10. package/dist/clients/revocation.d.ts +24 -9
  11. package/dist/clients/revocation.d.ts.map +1 -1
  12. package/dist/clients/revocation.js +23 -10
  13. package/dist/clients/revocation.js.map +1 -1
  14. package/dist/clients/rosterPolicy.d.ts +23 -22
  15. package/dist/clients/rosterPolicy.d.ts.map +1 -1
  16. package/dist/clients/rosterPolicy.js +50 -31
  17. package/dist/clients/rosterPolicy.js.map +1 -1
  18. package/dist/descriptors/acquire.d.ts +4 -1
  19. package/dist/descriptors/acquire.d.ts.map +1 -1
  20. package/dist/descriptors/acquire.js +29 -1
  21. package/dist/descriptors/acquire.js.map +1 -1
  22. package/dist/descriptors/index.d.ts +9 -1
  23. package/dist/descriptors/index.d.ts.map +1 -1
  24. package/dist/descriptors/index.js +9 -1
  25. package/dist/descriptors/index.js.map +1 -1
  26. package/dist/descriptors/logSource.d.ts +33 -0
  27. package/dist/descriptors/logSource.d.ts.map +1 -0
  28. package/dist/descriptors/logSource.js +49 -0
  29. package/dist/descriptors/logSource.js.map +1 -0
  30. package/dist/enrollment/enrollment.d.ts +2 -2
  31. package/dist/enrollment/enrollment.d.ts.map +1 -1
  32. package/dist/enrollment/enrollment.js +15 -10
  33. package/dist/enrollment/enrollment.js.map +1 -1
  34. package/dist/index.d.ts +1 -1
  35. package/dist/index.js +1 -1
  36. package/dist/keys/index.d.ts +15 -8
  37. package/dist/keys/index.d.ts.map +1 -1
  38. package/dist/keys/index.js +14 -8
  39. package/dist/keys/index.js.map +1 -1
  40. package/dist/keys/rosterLogStore.d.ts +53 -0
  41. package/dist/keys/rosterLogStore.d.ts.map +1 -0
  42. package/dist/keys/rosterLogStore.js +134 -0
  43. package/dist/keys/rosterLogStore.js.map +1 -0
  44. package/dist/keys/rosterStore.d.ts +20 -6
  45. package/dist/keys/rosterStore.d.ts.map +1 -1
  46. package/dist/keys/rosterStore.js +36 -17
  47. package/dist/keys/rosterStore.js.map +1 -1
  48. package/dist/keys/userKeyCascade.d.ts +14 -7
  49. package/dist/keys/userKeyCascade.d.ts.map +1 -1
  50. package/dist/keys/userKeyCascade.js +12 -3
  51. package/dist/keys/userKeyCascade.js.map +1 -1
  52. package/dist/keys/userKeyRoster.d.ts +50 -97
  53. package/dist/keys/userKeyRoster.d.ts.map +1 -1
  54. package/dist/keys/userKeyRoster.js +44 -145
  55. package/dist/keys/userKeyRoster.js.map +1 -1
  56. package/dist/request/appKey.d.ts +255 -0
  57. package/dist/request/appKey.d.ts.map +1 -0
  58. package/dist/request/appKey.js +492 -0
  59. package/dist/request/appKey.js.map +1 -0
  60. package/dist/request/classify.d.ts +54 -3
  61. package/dist/request/classify.d.ts.map +1 -1
  62. package/dist/request/classify.js +106 -0
  63. package/dist/request/classify.js.map +1 -1
  64. package/dist/request/composeVp.d.ts +6 -7
  65. package/dist/request/composeVp.d.ts.map +1 -1
  66. package/dist/request/composeVp.js +44 -55
  67. package/dist/request/composeVp.js.map +1 -1
  68. package/dist/request/index.d.ts +4 -0
  69. package/dist/request/index.d.ts.map +1 -1
  70. package/dist/request/index.js +4 -0
  71. package/dist/request/index.js.map +1 -1
  72. package/dist/request/processRequest.d.ts +1 -4
  73. package/dist/request/processRequest.d.ts.map +1 -1
  74. package/dist/request/processRequest.js +17 -11
  75. package/dist/request/processRequest.js.map +1 -1
  76. package/dist/request/types.d.ts +45 -2
  77. package/dist/request/types.d.ts.map +1 -1
  78. package/dist/resourceLog/append.d.ts +103 -0
  79. package/dist/resourceLog/append.d.ts.map +1 -0
  80. package/dist/resourceLog/append.js +190 -0
  81. package/dist/resourceLog/append.js.map +1 -0
  82. package/dist/resourceLog/controller.d.ts +57 -0
  83. package/dist/resourceLog/controller.d.ts.map +1 -0
  84. package/dist/resourceLog/controller.js +81 -0
  85. package/dist/resourceLog/controller.js.map +1 -0
  86. package/dist/resourceLog/entry.d.ts +68 -0
  87. package/dist/resourceLog/entry.d.ts.map +1 -0
  88. package/dist/resourceLog/entry.js +167 -0
  89. package/dist/resourceLog/entry.js.map +1 -0
  90. package/dist/resourceLog/errors.d.ts +75 -0
  91. package/dist/resourceLog/errors.d.ts.map +1 -0
  92. package/dist/resourceLog/errors.js +76 -0
  93. package/dist/resourceLog/errors.js.map +1 -0
  94. package/dist/resourceLog/index.d.ts +26 -0
  95. package/dist/resourceLog/index.d.ts.map +1 -0
  96. package/dist/resourceLog/index.js +26 -0
  97. package/dist/resourceLog/index.js.map +1 -0
  98. package/dist/resourceLog/pin.d.ts +44 -0
  99. package/dist/resourceLog/pin.d.ts.map +1 -0
  100. package/dist/resourceLog/pin.js +32 -0
  101. package/dist/resourceLog/pin.js.map +1 -0
  102. package/dist/resourceLog/seal.d.ts +86 -0
  103. package/dist/resourceLog/seal.d.ts.map +1 -0
  104. package/dist/resourceLog/seal.js +99 -0
  105. package/dist/resourceLog/seal.js.map +1 -0
  106. package/dist/resourceLog/verify.d.ts +95 -0
  107. package/dist/resourceLog/verify.d.ts.map +1 -0
  108. package/dist/resourceLog/verify.js +435 -0
  109. package/dist/resourceLog/verify.js.map +1 -0
  110. package/dist/space/collections.d.ts +14 -11
  111. package/dist/space/collections.d.ts.map +1 -1
  112. package/dist/space/collections.js +14 -11
  113. package/dist/space/collections.js.map +1 -1
  114. package/dist/space/index.d.ts +2 -2
  115. package/dist/space/index.d.ts.map +1 -1
  116. package/dist/space/index.js +2 -2
  117. package/dist/space/index.js.map +1 -1
  118. package/dist/webvh/didWeb.d.ts +4 -6
  119. package/dist/webvh/didWeb.d.ts.map +1 -1
  120. package/dist/webvh/didWeb.js.map +1 -1
  121. package/dist/webvh/didWebvh.d.ts +1 -2
  122. package/dist/webvh/didWebvh.d.ts.map +1 -1
  123. package/dist/webvh/didWebvh.js +1 -6
  124. package/dist/webvh/didWebvh.js.map +1 -1
  125. package/package.json +11 -4
@@ -0,0 +1,492 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The App Connect app-key credential: a self-issued VC that carries a 32-byte
6
+ * seed an app keeps in the user's wallet so it can open its encrypted data on
7
+ * this and other devices. Unlike an ordinary credential, the issuer and
8
+ * subject are NOT the wallet user's DID -- they are a did:key derived from the
9
+ * seed itself (self-issued by the app key), so the credential validates
10
+ * standalone and the same seed reconstitutes the same identity on every
11
+ * client. The credential is bound to the attested requesting origin
12
+ * (`credentialSubject.origin`) and to the application's canonical URL
13
+ * (`credentialSubject.appUrl`), and matched wallet-side against both, so a
14
+ * phishing origin can neither recover an existing app key nor be handed one
15
+ * minted for another origin, and applications sharing an origin are kept
16
+ * apart by their `appUrl`s.
17
+ *
18
+ * Ported from Freewallet's `src/lib/appKey.ts`, migrated from the
19
+ * `credentialType` / `vocabBase` model to the App Connect spec's `appUrl`
20
+ * model: the type array is a fixed two-entry list, the `@context` is the VC
21
+ * 1.1 context URL followed by the hosted App Connect context URL (identical
22
+ * for every application), and which application a credential belongs to is a
23
+ * claim (`credentialSubject.appUrl`), not a type. See the App Connect spec's
24
+ * App Key Credential section for the normative shape, matching, and minting
25
+ * rules this module implements.
26
+ */
27
+ import * as vc from '@interop/vc';
28
+ import { base64urlnopad } from '@scure/base';
29
+ import { CapabilityAgent } from '@interop/webkms-client';
30
+ import { Ed25519Signature2020 } from '@interop/ed25519-signature';
31
+ import { issuerId, subjectId, typeArray } from '@interop/data-integrity-core/guards';
32
+ import { CONTEXT_URL_V1 as APP_CONNECT_CONTEXT_URL } from 'byoe-context';
33
+ import { documentLoader } from './composeVp.js';
34
+ /**
35
+ * The semantic `handle` mixed into seed derivation. It identifies the agent
36
+ * but does not affect the derived key (only the seed bytes and the key name
37
+ * enter the HMAC), so it is cosmetic; kept for legibility.
38
+ */
39
+ const APP_KEY_HANDLE = 'freewallet-app-key';
40
+ /**
41
+ * The key name mixed into seed derivation. Unlike the handle this is
42
+ * load-bearing: it is the HMAC message in `CapabilityAgent` derivation, so the
43
+ * exact string is a pinned input of the App Connect key-derivation rule --
44
+ * every existing app-key credential's identity depends on it.
45
+ */
46
+ export const APP_KEY_KEY_NAME = 'app-key';
47
+ const VC_1_CONTEXT_URL = 'https://www.w3.org/2018/credentials/v1';
48
+ /**
49
+ * The marker type every app-key credential carries, mapped to one stable IRI
50
+ * for every application. It makes "presents as an app key" a term check rather
51
+ * than a shape heuristic, which is what the store-time refusal
52
+ * ({@link assertStorableAppKey}) and the match path key off.
53
+ *
54
+ * It is a self-declaration, not evidence: the `type` array of a planted
55
+ * credential is attacker-controlled like the rest of it. The seed-to-subject
56
+ * binding ({@link appKeySeedBindsSubject}) authenticates only a credential's
57
+ * internal consistency, never its provenance (a fully attacker-generated
58
+ * credential binds perfectly). That is exactly why external ingest refuses on
59
+ * the marker alone, binding or not ({@link assertStorableAppKey}): app keys
60
+ * are wallet-minted, never imported.
61
+ */
62
+ export const APP_KEY_CREDENTIAL_TYPE = 'AppKeyCredential';
63
+ /**
64
+ * The fixed `type` array of every app-key credential: exactly two entries, in
65
+ * this order, identical for every application. Which application a credential
66
+ * belongs to is the `credentialSubject.appUrl` claim, not a type.
67
+ */
68
+ export const APP_KEY_TYPE_ARRAY = Object.freeze([
69
+ 'VerifiableCredential',
70
+ APP_KEY_CREDENTIAL_TYPE
71
+ ]);
72
+ /**
73
+ * The hosted App Connect context URL every app-key credential carries as the
74
+ * second entry of its `@context` array (after the VC 1.1 context URL). The
75
+ * context document defines the profile's BYOE terms; document loaders resolve
76
+ * it from the bundled `byoe-context` document, so no fetch happens at sign or
77
+ * verification time.
78
+ */
79
+ export { APP_CONNECT_CONTEXT_URL };
80
+ /**
81
+ * The number of random bytes in an app-key seed.
82
+ */
83
+ const SEED_BYTE_LENGTH = 32;
84
+ /**
85
+ * Whether a credential presents as an app key -- that is, carries the
86
+ * {@link APP_KEY_CREDENTIAL_TYPE} marker in its `type` array. Says nothing
87
+ * about whether it IS one; that is the seed-to-subject binding's job.
88
+ *
89
+ * @param credential {IVerifiableCredential}
90
+ * @returns {boolean}
91
+ */
92
+ export function presentsAsAppKey(credential) {
93
+ return typeArray(credential.type).includes(APP_KEY_CREDENTIAL_TYPE);
94
+ }
95
+ /**
96
+ * The refusal a store path raises for a credential that presents as an app key
97
+ * but arrived from outside the wallet's own mint path. A distinct class so the
98
+ * UI can show its own translated wording rather than this message.
99
+ */
100
+ export class AppKeyRefusedError extends Error {
101
+ constructor() {
102
+ super('This credential claims to be an app key. App keys are created by the ' +
103
+ 'wallet itself and cannot be added from outside, so it was not stored.');
104
+ this.name = 'AppKeyRefusedError';
105
+ }
106
+ }
107
+ /**
108
+ * Refuses any credential that presents as an app key, unconditionally --
109
+ * whether or not it binds to its own seed. Called on every path that puts a
110
+ * credential in the store from outside the wallet (a CHAPI store offer, a
111
+ * URL / QR / manual-paste import), so an externally arriving app key never
112
+ * reaches the store, the credential list, or the user's Space.
113
+ *
114
+ * The seed-to-subject binding ({@link appKeySeedBindsSubject}) authenticates
115
+ * only the credential's internal consistency, not its provenance: a fully
116
+ * attacker-generated credential binds perfectly (a fresh seed, the victim
117
+ * app's `origin` and `appUrl`, self-issued), and storing it would make its
118
+ * DID the controller the wallet delegates the user's storage to. So there is
119
+ * no "binds, so it stores" carve-out here: app-key credentials are
120
+ * wallet-minted, never imported, and only the wallet's own mint path may
121
+ * store one.
122
+ *
123
+ * A credential with no marker is left alone, so an ordinary credential that
124
+ * merely happens to carry a `seed` or `origin` claim is never caught.
125
+ *
126
+ * @param credential {IVerifiableCredential}
127
+ * @returns {void} throws the refusal reason
128
+ */
129
+ export function assertStorableAppKey(credential) {
130
+ if (presentsAsAppKey(credential)) {
131
+ throw new AppKeyRefusedError();
132
+ }
133
+ }
134
+ /**
135
+ * Whether an app-key credential's subject DID is the one its own seed derives
136
+ * -- the binding that makes the credential an app key rather than merely a
137
+ * self-issued claim to be one. Self-issuance is a weak signal (anyone can
138
+ * self-issue); this is the strong one, and it is fully local: the credential
139
+ * carries the seed, so re-derive with the same call `mintAppKeyCredential`
140
+ * uses and compare. Fails closed on an absent, non-base64url, or otherwise
141
+ * unusable seed rather than throwing out of the match path.
142
+ *
143
+ * @param credential {IVerifiableCredential}
144
+ * @returns {Promise<boolean>}
145
+ */
146
+ export async function appKeySeedBindsSubject(credential) {
147
+ const subjectDid = subjectId(credential);
148
+ const seed = appKeySeedBytes(credential);
149
+ if (!subjectDid || !seed || seed.length !== SEED_BYTE_LENGTH) {
150
+ return false;
151
+ }
152
+ try {
153
+ const agent = await CapabilityAgent.fromSeed({
154
+ seed,
155
+ handle: APP_KEY_HANDLE,
156
+ keyName: APP_KEY_KEY_NAME
157
+ });
158
+ return agent.id === subjectDid;
159
+ }
160
+ catch {
161
+ return false;
162
+ }
163
+ }
164
+ /**
165
+ * Raised by {@link assertMintedAppKey} when a credential offered to the mint
166
+ * path's store door does not carry the mint invariants. Reaching it means a
167
+ * caller tried to route a foreign credential through the wallet's own mint
168
+ * door -- a programming error, not a user-facing refusal, so it is not
169
+ * translated like {@link AppKeyRefusedError}.
170
+ */
171
+ export class AppKeyMintInvariantError extends Error {
172
+ constructor() {
173
+ super('Only a wallet-minted app-key credential (marker type present, subject ' +
174
+ 'DID derived from its own seed) can be stored through the mint path.');
175
+ this.name = 'AppKeyMintInvariantError';
176
+ }
177
+ }
178
+ /**
179
+ * Asserts the mint invariants on a credential the wallet claims to have just
180
+ * minted: it presents as an app key (the marker type) and its subject DID
181
+ * re-derives from the seed it carries. The mirror image of
182
+ * {@link assertStorableAppKey} -- external ingest refuses every marker
183
+ * credential, the mint door stores only credentials that carry the full mint
184
+ * shape -- kept beside it so the two halves of the app-key store policy live
185
+ * in one module.
186
+ *
187
+ * @param credential {IVerifiableCredential}
188
+ * @returns {Promise<void>} throws {@link AppKeyMintInvariantError}
189
+ */
190
+ export async function assertMintedAppKey(credential) {
191
+ if (!presentsAsAppKey(credential) ||
192
+ !(await appKeySeedBindsSubject(credential))) {
193
+ throw new AppKeyMintInvariantError();
194
+ }
195
+ }
196
+ /**
197
+ * Whether a credential is self-issued: it names an issuer, and that issuer is
198
+ * its own subject. The shape every app-key credential has and every planted
199
+ * one must not be allowed to fake alone.
200
+ */
201
+ function isSelfIssued(credential) {
202
+ const issuer = issuerId(credential.issuer);
203
+ return !!issuer && issuer === subjectId(credential);
204
+ }
205
+ /**
206
+ * The instant a credential's `issuanceDate` denotes, or NaN when it is
207
+ * absent, not a string, or does not parse. Ranking is over instants, not raw
208
+ * strings: the ranking decides which DID the wallet delegates to, so a
209
+ * comparison manipulable by the *spelling* of a date (a numeric offset,
210
+ * differing fractional-second precision) would reopen the planted-credential
211
+ * path in a narrower form.
212
+ */
213
+ function issuanceInstant(credential) {
214
+ const raw = credential.issuanceDate;
215
+ if (typeof raw !== 'string') {
216
+ return NaN;
217
+ }
218
+ return Date.parse(raw);
219
+ }
220
+ /**
221
+ * Sort comparator ordering credentials latest-first by the instant their
222
+ * `issuanceDate` denotes; a credential whose date is absent or unparseable
223
+ * sorts last.
224
+ */
225
+ function byIssuanceInstantDesc(first, second) {
226
+ const firstInstant = issuanceInstant(first);
227
+ const secondInstant = issuanceInstant(second);
228
+ if (Number.isNaN(firstInstant)) {
229
+ return Number.isNaN(secondInstant) ? 0 : 1;
230
+ }
231
+ if (Number.isNaN(secondInstant)) {
232
+ return -1;
233
+ }
234
+ return secondInstant - firstInstant;
235
+ }
236
+ /**
237
+ * The app-key candidates for an app + origin, latest-first: everything the
238
+ * cheap, synchronous predicates accept, so only plausible candidates pay for
239
+ * a key derivation. A candidate must carry the marker (required, not merely
240
+ * tolerated: a credential can then only reach the delegation path by carrying
241
+ * it, which is exactly what the store-time refusal screens), name the
242
+ * request's `appUrl` in `credentialSubject.appUrl` (both sides in serialized
243
+ * form -- the wallet mints the claim serialized, so the comparison is an
244
+ * exact string match), be self-issued, and be bound to the attested
245
+ * requesting origin. Sorting here (rather than after the binding check) lets
246
+ * {@link findAppKeyCredential} stop at the newest credential that binds.
247
+ *
248
+ * @param options {object}
249
+ * @param options.credentials {IVerifiableCredential[]}
250
+ * @param options.appUrl {string} - The request's `app.appUrl` in serialized
251
+ * form.
252
+ * @param options.origin {string} - The attested requesting origin.
253
+ * @returns {IVerifiableCredential[]}
254
+ */
255
+ export function appKeyCandidates({ credentials, appUrl, origin }) {
256
+ return credentials
257
+ .filter(credential => presentsAsAppKey(credential) &&
258
+ appKeyAppUrl(credential) === appUrl &&
259
+ isSelfIssued(credential) &&
260
+ appKeyOrigin(credential) === origin)
261
+ .sort(byIssuanceInstantDesc);
262
+ }
263
+ /**
264
+ * The current (latest) app-key credential for an app + origin, or undefined
265
+ * when the user has none -- which signals first run for that (origin,
266
+ * `appUrl`) pair.
267
+ *
268
+ * @param options {object}
269
+ * @param options.credentials {IVerifiableCredential[]}
270
+ * @param options.appUrl {string} - The request's `app.appUrl` in serialized
271
+ * form.
272
+ * @param options.origin {string} - The attested requesting origin.
273
+ * @returns {Promise<IVerifiableCredential | undefined>}
274
+ */
275
+ export async function findAppKeyCredential({ credentials, appUrl, origin }) {
276
+ // Newest-first, returning at the first credential that binds: a non-binding
277
+ // one ranked above it is discarded on the way, and the credentials below it
278
+ // never pay for a key derivation.
279
+ for (const candidate of appKeyCandidates({ credentials, appUrl, origin })) {
280
+ if (await appKeySeedBindsSubject(candidate)) {
281
+ return candidate;
282
+ }
283
+ }
284
+ return undefined;
285
+ }
286
+ /**
287
+ * Assembles and signs an app-key credential for a seed the caller supplies:
288
+ * the fixed two-entry type array, the hosted App Connect context URL, issuer
289
+ * and subject both the seed-derived DID, and the `seed` / `appUrl` / `origin`
290
+ * claims. `vc.issue` auto-fills `issuanceDate` in the canonical UTC form the
291
+ * ranking expects. Shared by the fresh mint and the legacy re-issue.
292
+ */
293
+ async function issueAppKeyCredential({ seedBytes, appName, appUrl, origin, description }) {
294
+ const agent = await CapabilityAgent.fromSeed({
295
+ seed: seedBytes,
296
+ handle: APP_KEY_HANDLE,
297
+ keyName: APP_KEY_KEY_NAME
298
+ });
299
+ const controllerDid = agent.id;
300
+ const credential = {
301
+ '@context': [VC_1_CONTEXT_URL, APP_CONNECT_CONTEXT_URL],
302
+ id: `urn:uuid:${crypto.randomUUID()}`,
303
+ type: [...APP_KEY_TYPE_ARRAY],
304
+ name: `${appName} app key`,
305
+ description,
306
+ issuer: controllerDid,
307
+ credentialSubject: {
308
+ id: controllerDid,
309
+ seed: base64urlnopad.encode(seedBytes),
310
+ appUrl,
311
+ origin
312
+ }
313
+ };
314
+ const suite = new Ed25519Signature2020({ signer: agent.getSigner() });
315
+ const signed = (await vc.issue({
316
+ credential,
317
+ suite,
318
+ documentLoader
319
+ }));
320
+ return { credential: signed, subjectDid: controllerDid };
321
+ }
322
+ /**
323
+ * Mints a fresh app-key credential for an app + origin: generates a 32-byte
324
+ * seed, derives the seed's did:key, and self-issues the credential (issuer ==
325
+ * subject == the seed-derived DID) with the `credentialSubject.appUrl` claim
326
+ * set from the validated request value. Does NOT store the result -- the
327
+ * caller stores it before delegating, so a failed delegation is found as
328
+ * "returning" on the next attempt rather than minting a second identity.
329
+ *
330
+ * @param options {object}
331
+ * @param options.app {IAppConnectApp} - The validated app identity; its
332
+ * `appUrl` must already be in serialized form (`appConnectRequestOf`
333
+ * guarantees this).
334
+ * @param options.origin {string} - The attested requesting origin, never a
335
+ * value taken from the request body.
336
+ * @returns {Promise<{ credential: IVerifiableCredential; subjectDid: string }>}
337
+ */
338
+ export async function mintAppKeyCredential({ app, origin }) {
339
+ const seedBytes = crypto.getRandomValues(new Uint8Array(SEED_BYTE_LENGTH));
340
+ return issueAppKeyCredential({
341
+ seedBytes,
342
+ appName: app.name,
343
+ appUrl: app.appUrl,
344
+ origin,
345
+ description: `The ${app.name} app keeps this key in your wallet so it can open ` +
346
+ 'your encrypted data on this and other devices.'
347
+ });
348
+ }
349
+ /**
350
+ * The latest legacy app-key credential for an origin, or undefined. A legacy
351
+ * credential predates the `appUrl` model: it carries the marker and a
352
+ * per-app third type entry, but no `credentialSubject.appUrl` claim. Its
353
+ * identity was scoped to (origin, `credentialType`), and the request no
354
+ * longer carries a `credentialType` to select on, so the mapping is
355
+ * recoverable only when it is unambiguous: when the binding legacy candidates
356
+ * for the origin all name one subject DID. Two distinct legacy identities on
357
+ * one origin yield undefined rather than a guess -- re-issuing the wrong one
358
+ * would hand one application another's identity, which is worse than the
359
+ * caller treating the connect as a first run.
360
+ *
361
+ * @param options {object}
362
+ * @param options.credentials {IVerifiableCredential[]}
363
+ * @param options.origin {string} - The attested requesting origin.
364
+ * @returns {Promise<IVerifiableCredential | undefined>}
365
+ */
366
+ export async function findLegacyAppKeyCredential({ credentials, origin }) {
367
+ const candidates = credentials
368
+ .filter(credential => presentsAsAppKey(credential) &&
369
+ appKeyAppUrl(credential) === undefined &&
370
+ isSelfIssued(credential) &&
371
+ appKeyOrigin(credential) === origin)
372
+ .sort(byIssuanceInstantDesc);
373
+ const bound = [];
374
+ for (const candidate of candidates) {
375
+ if (await appKeySeedBindsSubject(candidate)) {
376
+ bound.push(candidate);
377
+ }
378
+ }
379
+ if (bound.length === 0) {
380
+ return undefined;
381
+ }
382
+ const subjects = new Set(bound.map(credential => subjectId(credential)));
383
+ if (subjects.size > 1) {
384
+ return undefined;
385
+ }
386
+ return bound[0];
387
+ }
388
+ /**
389
+ * Re-issues a legacy app-key credential in place under the `appUrl` model:
390
+ * the same seed (so the same derived DID -- the app's identity and its
391
+ * encrypted-data access are preserved), the fixed two-entry type array, the
392
+ * hosted App Connect context URL, and the `credentialSubject.appUrl` claim set
393
+ * from the validated request value. A fresh mint would roll the seed and orphan
394
+ * the identity, and must never be the migration path. The fresh
395
+ * `issuanceDate` ranks the re-issued credential ahead of the legacy one, so
396
+ * subsequent connects match it directly.
397
+ *
398
+ * Does NOT store the result; the caller stores it through the mint door
399
+ * (it satisfies {@link assertMintedAppKey}) and may retire the legacy record.
400
+ *
401
+ * @param options {object}
402
+ * @param options.credential {IVerifiableCredential} - The legacy credential;
403
+ * it must bind per {@link appKeySeedBindsSubject} and be bound to `origin`,
404
+ * else this throws {@link AppKeyMintInvariantError}.
405
+ * @param options.app {IAppConnectApp} - The validated app identity; its
406
+ * `appUrl` must already be in serialized form.
407
+ * @param options.origin {string} - The attested requesting origin.
408
+ * @returns {Promise<{ credential: IVerifiableCredential; subjectDid: string }>}
409
+ */
410
+ export async function reissueAppKeyCredential({ credential, app, origin }) {
411
+ if (!(await appKeySeedBindsSubject(credential)) ||
412
+ appKeyOrigin(credential) !== origin) {
413
+ throw new AppKeyMintInvariantError();
414
+ }
415
+ const seedBytes = appKeySeedBytes(credential);
416
+ if (!seedBytes) {
417
+ throw new AppKeyMintInvariantError();
418
+ }
419
+ const legacyDescription = credential
420
+ .description;
421
+ return issueAppKeyCredential({
422
+ seedBytes,
423
+ appName: app.name,
424
+ appUrl: app.appUrl,
425
+ origin,
426
+ description: typeof legacyDescription === 'string'
427
+ ? legacyDescription
428
+ : `The ${app.name} app keeps this key in your wallet so it can ` +
429
+ 'open your encrypted data on this and other devices.'
430
+ });
431
+ }
432
+ /**
433
+ * The subject DID (`credentialSubject.id`) of an app-key credential, or
434
+ * undefined. For a valid app-key credential this equals the issuer.
435
+ *
436
+ * @param credential {IVerifiableCredential}
437
+ * @returns {string | undefined}
438
+ */
439
+ export function appKeySubjectDid(credential) {
440
+ return subjectId(credential);
441
+ }
442
+ /**
443
+ * The 32-byte seed an app-key credential carries
444
+ * (`credentialSubject.seed`, base64url-no-pad), or undefined when it is absent
445
+ * or malformed. This is the app's client secret, the root of its identity and
446
+ * of the keys it encrypts its own data with. The wallet reads it only to
447
+ * re-derive the credential's subject DID ({@link appKeySeedBindsSubject}) and
448
+ * to re-issue a legacy credential under the same identity; nothing downstream
449
+ * of the match takes the seed, so it never reaches the grant path.
450
+ *
451
+ * @param credential {IVerifiableCredential}
452
+ * @returns {Uint8Array | undefined}
453
+ */
454
+ function appKeySeedBytes(credential) {
455
+ const subject = credential.credentialSubject;
456
+ if (!subject || typeof subject.seed !== 'string') {
457
+ return undefined;
458
+ }
459
+ try {
460
+ return base64urlnopad.decode(subject.seed);
461
+ }
462
+ catch {
463
+ return undefined;
464
+ }
465
+ }
466
+ /**
467
+ * The origin (`credentialSubject.origin`) an app-key credential is bound to,
468
+ * when present.
469
+ *
470
+ * @param credential {IVerifiableCredential}
471
+ * @returns {string | undefined}
472
+ */
473
+ export function appKeyOrigin(credential) {
474
+ const subject = credential.credentialSubject;
475
+ return subject && typeof subject.origin === 'string'
476
+ ? subject.origin
477
+ : undefined;
478
+ }
479
+ /**
480
+ * The application URL (`credentialSubject.appUrl`) an app-key credential is
481
+ * scoped to, when present. Absent on a legacy (pre-`appUrl`) credential.
482
+ *
483
+ * @param credential {IVerifiableCredential}
484
+ * @returns {string | undefined}
485
+ */
486
+ export function appKeyAppUrl(credential) {
487
+ const subject = credential.credentialSubject;
488
+ return subject && typeof subject.appUrl === 'string'
489
+ ? subject.appUrl
490
+ : undefined;
491
+ }
492
+ //# sourceMappingURL=appKey.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appKey.js","sourceRoot":"","sources":["../../src/request/appKey.ts"],"names":[],"mappings":"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"}
@@ -9,10 +9,11 @@
9
9
  *
10
10
  * Ported from Freewallet's `src/lib/walletRequest/classify.ts` (the superset of
11
11
  * DCW's `app/lib/exchanges.ts` / `walletRequestApi.ts` dispatch helpers). The
12
- * App Connect query kind is a Freewallet-only extension and stays app-side; the
13
- * shared classifier covers the three VPR-spec query types.
12
+ * shared classifier covers the three VPR-spec query types plus the App Connect
13
+ * `AppConnectQuery` extension (`appConnectRequestOf`), whose `app` block is
14
+ * validated here against the attested requesting origin.
14
15
  */
15
- import type { CHAPIStoreEvent, ICapabilityQueryDetail, ICredentialQuery, IQueryByExample, IVPOffer, IVPRequest, IVPRDetails, IVPRQuery, IVerifiableCredential, IVerifiablePresentation, WalletRequestProfile } from './types.js';
16
+ import type { CHAPIStoreEvent, IAppConnectRequest, ICapabilityQueryDetail, ICredentialQuery, IQueryByExample, IVPOffer, IVPRequest, IVPRDetails, IVPRQuery, IVerifiableCredential, IVerifiablePresentation, WalletRequestProfile } from './types.js';
16
17
  import type { CHAPIGetEvent } from './types.js';
17
18
  /**
18
19
  * The VC data model version a presentation carrying the given credentials must
@@ -90,6 +91,56 @@ export declare function credentialQueriesOf(query: IQueryByExample): ICredential
90
91
  * @returns {ICapabilityQueryDetail[]}
91
92
  */
92
93
  export declare function zcapQueriesOf(queries: IVPRQuery[]): ICapabilityQueryDetail[];
94
+ /**
95
+ * Validates an App Connect `app.appUrl` against the attested requesting origin
96
+ * and returns its serialized form. The value must parse as an absolute URL,
97
+ * must not carry a fragment, and its origin must equal the attested origin;
98
+ * any violation throws (the query is malformed). All storage and comparison
99
+ * downstream uses the returned serialization, so spellings differing only in
100
+ * a default port, percent-encoding case, or dot-segments do not name distinct
101
+ * applications.
102
+ *
103
+ * The fragment check reads the serialized URL rather than `url.hash`: a bare
104
+ * trailing `#` sets an empty (non-null) fragment that `hash` reports as `''`,
105
+ * and a percent-encoded `%23` never appears as `#` in the serialization.
106
+ *
107
+ * @param options {object}
108
+ * @param options.appUrl {string} - The request's `app.appUrl`.
109
+ * @param options.origin {string} - The attested requesting origin.
110
+ * @returns {string} The parsed URL's serialization.
111
+ */
112
+ export declare function serializedAppUrl({ appUrl, origin }: {
113
+ appUrl: string;
114
+ origin: string;
115
+ }): string;
116
+ /**
117
+ * Extracts the App Connect request from a query set, when one is present. An
118
+ * `AppConnectQuery` is one mental model per popup: the request must not also
119
+ * carry `QueryByExample` or standalone zcap queries, at most one
120
+ * `AppConnectQuery` is allowed, and its `app` block must carry the display
121
+ * `name` and the `appUrl` the wallet needs to match or mint the app-key
122
+ * credential -- with the `appUrl` validated against the attested requesting
123
+ * origin and rewritten to its serialized form ({@link serializedAppUrl}).
124
+ * Violations throw; classification-time callers surface the throw as a
125
+ * malformed-request state. The capability queries are normalized to an array
126
+ * (absent means "no grants requested" -- a connect that only recovers the app
127
+ * key is legal), and each entry is rebuilt from an allowlist of the declared
128
+ * fields (`referenceId`, `allowedAction`, `invocationTarget`): the type-level
129
+ * Omit does not bind an actual request body, so any other wire-level field --
130
+ * a smuggled `reason`, an attacker-chosen `controller`, a future
131
+ * display-bearing addition -- is made unrepresentable here, before the entries
132
+ * reach the profile the consent screen and the delegation path read.
133
+ *
134
+ * @param options {object}
135
+ * @param options.queries {IVPRQuery[]}
136
+ * @param options.origin {string} - The attested requesting origin the
137
+ * `appUrl` is validated against.
138
+ * @returns {IAppConnectRequest | null}
139
+ */
140
+ export declare function appConnectRequestOf({ queries, origin }: {
141
+ queries: IVPRQuery[];
142
+ origin: string;
143
+ }): IAppConnectRequest | null;
93
144
  /**
94
145
  * Classifies a VPR body onto the independent axes the consent screen and
95
146
  * response assembly work from: whether DID Authentication is requested, and
@@ -1 +1 @@
1
- {"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../../src/request/classify.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EACV,eAAe,EACf,sBAAsB,EACtB,gBAAgB,EAEhB,eAAe,EACf,QAAQ,EACR,UAAU,EACV,WAAW,EACX,SAAS,EACT,qBAAqB,EACrB,uBAAuB,EAEvB,oBAAoB,EACrB,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAO/C;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,qBAAqB,EAAE,GACnC,MAAM,CAOR;AA0DD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,YAAY,EAAE,uBAAuB,GACpC,qBAAqB,EAAE,CAQzB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAYtE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,eAAe,GAAG,QAAQ,CAKxE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACR,EAAE;IACD,OAAO,EAAE,SAAS,EAAE,CAAA;CACrB,GAAG,OAAO,CAQV;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,EAAE,CAS3D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,eAAe,GACrB,gBAAgB,EAAE,CAMpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,sBAAsB,EAAE,CAoB5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,oBAAoB,CAS1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAMpE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,CAQpE"}
1
+ {"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../../src/request/classify.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EACV,eAAe,EAGf,kBAAkB,EAClB,sBAAsB,EACtB,gBAAgB,EAEhB,eAAe,EACf,QAAQ,EACR,UAAU,EACV,WAAW,EACX,SAAS,EACT,qBAAqB,EACrB,uBAAuB,EAEvB,oBAAoB,EACrB,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAO/C;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,qBAAqB,EAAE,GACnC,MAAM,CAOR;AA0DD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,YAAY,EAAE,uBAAuB,GACpC,qBAAqB,EAAE,CAQzB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAYtE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,eAAe,GAAG,QAAQ,CAKxE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACR,EAAE;IACD,OAAO,EAAE,SAAS,EAAE,CAAA;CACrB,GAAG,OAAO,CAQV;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,EAAE,CAS3D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,eAAe,GACrB,gBAAgB,EAAE,CAMpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,sBAAsB,EAAE,CAoB5E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,MAAM,EACP,EAAE;IACD,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,MAAM,CA4BT;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,MAAM,EACP,EAAE;IACD,OAAO,EAAE,SAAS,EAAE,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,kBAAkB,GAAG,IAAI,CAmD5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,oBAAoB,CAS1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAMpE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,CAQpE"}