@oma3/omatrust 0.1.0-alpha.10 → 0.1.0-alpha.12

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 (48) hide show
  1. package/README.md +3 -0
  2. package/dist/app-registry/index.cjs +53 -0
  3. package/dist/app-registry/index.cjs.map +1 -1
  4. package/dist/app-registry/index.js +53 -0
  5. package/dist/app-registry/index.js.map +1 -1
  6. package/dist/identity/index.cjs +1028 -6
  7. package/dist/identity/index.cjs.map +1 -1
  8. package/dist/identity/index.d.cts +2 -1
  9. package/dist/identity/index.d.ts +2 -1
  10. package/dist/identity/index.js +984 -7
  11. package/dist/identity/index.js.map +1 -1
  12. package/dist/index-B5OC9_8B.d.cts +480 -0
  13. package/dist/index-Bu-xxcv9.d.ts +407 -0
  14. package/dist/index-C6WNgPRx.d.ts +480 -0
  15. package/dist/index-C7odEbp6.d.cts +407 -0
  16. package/dist/index.cjs +2498 -373
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +4 -3
  19. package/dist/index.d.ts +4 -3
  20. package/dist/index.js +2342 -236
  21. package/dist/index.js.map +1 -1
  22. package/dist/reputation/index.browser.cjs +949 -67
  23. package/dist/reputation/index.browser.cjs.map +1 -1
  24. package/dist/reputation/index.browser.d.cts +2 -2
  25. package/dist/reputation/index.browser.d.ts +2 -2
  26. package/dist/reputation/index.browser.js +947 -67
  27. package/dist/reputation/index.browser.js.map +1 -1
  28. package/dist/reputation/index.cjs +1745 -232
  29. package/dist/reputation/index.cjs.map +1 -1
  30. package/dist/reputation/index.d.cts +3 -2
  31. package/dist/reputation/index.d.ts +3 -2
  32. package/dist/reputation/index.js +1727 -232
  33. package/dist/reputation/index.js.map +1 -1
  34. package/dist/{subject-ownership-CXvzEjpH.d.cts → subject-ownership-B7cFlm8c.d.cts} +256 -26
  35. package/dist/{subject-ownership-CXvzEjpH.d.ts → subject-ownership-B7cFlm8c.d.ts} +256 -26
  36. package/dist/types-Dn0OwaNj.d.cts +307 -0
  37. package/dist/types-Dn0OwaNj.d.ts +307 -0
  38. package/dist/widgets/index.cjs +70 -27
  39. package/dist/widgets/index.cjs.map +1 -1
  40. package/dist/widgets/index.d.cts +45 -18
  41. package/dist/widgets/index.d.ts +45 -18
  42. package/dist/widgets/index.js +67 -26
  43. package/dist/widgets/index.js.map +1 -1
  44. package/package.json +5 -2
  45. package/dist/index-B9KW02US.d.cts +0 -119
  46. package/dist/index-DXrwBex9.d.ts +0 -119
  47. package/dist/index-QZDExA4I.d.cts +0 -90
  48. package/dist/index-QZDExA4I.d.ts +0 -90
package/dist/index.js CHANGED
@@ -1,5 +1,11 @@
1
- import { keccak256, toUtf8Bytes, isAddress, getAddress, sha256, ZeroAddress, Signature, formatUnits, verifyTypedData, hexlify, randomBytes, id, Contract, Interface } from 'ethers';
1
+ import { keccak256, toUtf8Bytes, isAddress, getAddress, sha256, ZeroAddress, Signature, formatUnits, verifyTypedData, Interface, Contract, hexlify, randomBytes, id } from 'ethers';
2
+ import { base64url, calculateJwkThumbprint, decodeProtectedHeader, importJWK, compactVerify } from 'jose';
2
3
  import canonicalize from 'canonicalize';
4
+ import { CID } from 'multiformats/cid';
5
+ import { sha256 as sha256$1 } from 'multiformats/hashes/sha2';
6
+ import * as raw from 'multiformats/codecs/raw';
7
+ import { base32 } from 'multiformats/bases/base32';
8
+ import { base58btc } from 'multiformats/bases/base58';
3
9
  import { SchemaEncoder, EAS } from '@ethereum-attestation-service/eas-sdk';
4
10
  import { resolveTxt } from 'dns/promises';
5
11
 
@@ -13,31 +19,31 @@ var __export = (target, all) => {
13
19
  var OmaTrustError = class extends Error {
14
20
  code;
15
21
  details;
16
- constructor(code, message, details) {
22
+ constructor(code2, message, details) {
17
23
  super(message);
18
24
  this.name = "OmaTrustError";
19
- this.code = code;
25
+ this.code = code2;
20
26
  this.details = details;
21
27
  }
22
28
  };
23
- function toOmaTrustError(code, message, details) {
24
- return new OmaTrustError(code, message, details);
29
+ function toOmaTrustError(code2, message, details) {
30
+ return new OmaTrustError(code2, message, details);
25
31
  }
26
32
 
27
33
  // src/shared/assert.ts
28
- function assertString(value, name, code = "INVALID_INPUT") {
34
+ function assertString(value, name, code2 = "INVALID_INPUT") {
29
35
  if (typeof value !== "string" || value.trim().length === 0) {
30
- throw new OmaTrustError(code, `${name} must be a non-empty string`, { value });
36
+ throw new OmaTrustError(code2, `${name} must be a non-empty string`, { value });
31
37
  }
32
38
  }
33
- function assertNumber(value, name, code = "INVALID_INPUT") {
39
+ function assertNumber(value, name, code2 = "INVALID_INPUT") {
34
40
  if (typeof value !== "number" || Number.isNaN(value)) {
35
- throw new OmaTrustError(code, `${name} must be a valid number`, { value });
41
+ throw new OmaTrustError(code2, `${name} must be a valid number`, { value });
36
42
  }
37
43
  }
38
- function assertObject(value, name, code = "INVALID_INPUT") {
44
+ function assertObject(value, name, code2 = "INVALID_INPUT") {
39
45
  if (!value || typeof value !== "object" || Array.isArray(value)) {
40
- throw new OmaTrustError(code, `${name} must be an object`, { value });
46
+ throw new OmaTrustError(code2, `${name} must be an object`, { value });
41
47
  }
42
48
  }
43
49
  function asError(err) {
@@ -50,37 +56,63 @@ function asError(err) {
50
56
  // src/identity/index.ts
51
57
  var identity_exports = {};
52
58
  __export(identity_exports, {
59
+ artifactDidFromBytes: () => artifactDidFromBytes,
60
+ artifactDidFromJson: () => artifactDidFromJson,
61
+ assertBareDid: () => assertBareDid,
62
+ assertJsonSafe: () => assertJsonSafe,
53
63
  buildCaip10: () => buildCaip10,
54
64
  buildCaip2: () => buildCaip2,
55
65
  buildDidPkh: () => buildDidPkh,
56
66
  buildDidPkhFromCaip10: () => buildDidPkhFromCaip10,
57
67
  buildDidWeb: () => buildDidWeb,
58
68
  buildEvmDidPkh: () => buildEvmDidPkh,
69
+ canonicalizeAndKeccak256: () => canonicalizeAndKeccak256,
59
70
  canonicalizeForHash: () => canonicalizeForHash,
60
71
  canonicalizeJson: () => canonicalizeJson,
61
72
  computeDidAddress: () => computeDidAddress,
62
73
  computeDidHash: () => computeDidHash,
74
+ computeJwkThumbprint: () => computeJwkThumbprint,
75
+ didEthrToDidPkh: () => didEthrToDidPkh,
76
+ didJwkToJwk: () => didJwkToJwk,
77
+ didKeyToDidJwk: () => didKeyToDidJwk,
63
78
  didToAddress: () => didToAddress,
64
79
  extractAddressFromDid: () => extractAddressFromDid,
80
+ extractAuthorizationMetadata: () => extractAuthorizationMetadata,
81
+ extractControllerEvmAddress: () => extractControllerEvmAddress,
65
82
  extractDidIdentifier: () => extractDidIdentifier,
66
83
  extractDidMethod: () => extractDidMethod,
84
+ formatJktValue: () => formatJktValue,
67
85
  getAddressFromDidPkh: () => getAddressFromDidPkh,
68
86
  getChainIdFromDidPkh: () => getChainIdFromDidPkh,
69
87
  getDomainFromDidWeb: () => getDomainFromDidWeb,
70
88
  getNamespaceFromDidPkh: () => getNamespaceFromDidPkh,
71
89
  hashCanonicalizedJson: () => hashCanonicalizedJson,
90
+ isDidUrl: () => isDidUrl,
72
91
  isEvmDidPkh: () => isEvmDidPkh,
92
+ isPrivateKeyDid: () => isPrivateKeyDid,
93
+ isSameControllerId: () => isSameControllerId,
73
94
  isValidDid: () => isValidDid,
95
+ jwkToDidJwk: () => jwkToDidJwk,
74
96
  normalizeCaip10: () => normalizeCaip10,
75
97
  normalizeDid: () => normalizeDid,
76
98
  normalizeDidHandle: () => normalizeDidHandle,
99
+ normalizeDidJwk: () => normalizeDidJwk,
77
100
  normalizeDidKey: () => normalizeDidKey,
78
101
  normalizeDidPkh: () => normalizeDidPkh,
79
102
  normalizeDidWeb: () => normalizeDidWeb,
80
103
  normalizeDomain: () => normalizeDomain,
104
+ parseArtifactDid: () => parseArtifactDid,
81
105
  parseCaip10: () => parseCaip10,
82
106
  parseCaip2: () => parseCaip2,
83
- validateDidAddress: () => validateDidAddress
107
+ parseDidUrl: () => parseDidUrl,
108
+ parseJsonStrict: () => parseJsonStrict,
109
+ publicJwkEquals: () => publicJwkEquals,
110
+ resolveDidUrlToControllerDid: () => resolveDidUrlToControllerDid,
111
+ resolveDidUrlToPublicKey: () => resolveDidUrlToPublicKey,
112
+ validateDidAddress: () => validateDidAddress,
113
+ validatePrivateKeyDid: () => validatePrivateKeyDid,
114
+ validatePublicJwk: () => validatePublicJwk,
115
+ verifyDidArtifact: () => verifyDidArtifact
84
116
  });
85
117
 
86
118
  // src/identity/caip.ts
@@ -136,6 +168,158 @@ function parseCaip2(caip2) {
136
168
  }
137
169
  return { namespace, reference };
138
170
  }
171
+ var VALID_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
172
+ var PRIVATE_KEY_FIELDS = /* @__PURE__ */ new Set(["d", "p", "q", "dp", "dq", "qi", "oth"]);
173
+ var REQUIRED_PUBLIC_FIELDS = {
174
+ EC: ["crv", "x", "y"],
175
+ OKP: ["crv", "x"],
176
+ RSA: ["n", "e"]
177
+ };
178
+ function validatePublicJwk(jwk) {
179
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
180
+ return { valid: false, error: "JWK must be a non-null object" };
181
+ }
182
+ const obj = jwk;
183
+ const kty = obj.kty;
184
+ if (typeof kty !== "string" || !VALID_KTY.has(kty)) {
185
+ return {
186
+ valid: false,
187
+ error: `Invalid or missing kty (must be one of: ${[...VALID_KTY].join(", ")})`
188
+ };
189
+ }
190
+ for (const field of PRIVATE_KEY_FIELDS) {
191
+ if (field in obj) {
192
+ return {
193
+ valid: false,
194
+ error: `JWK contains private key field "${field}" \u2014 only public keys are allowed`
195
+ };
196
+ }
197
+ }
198
+ const required = REQUIRED_PUBLIC_FIELDS[kty];
199
+ if (required) {
200
+ for (const field of required) {
201
+ if (!(field in obj) || obj[field] === void 0 || obj[field] === null || obj[field] === "") {
202
+ return {
203
+ valid: false,
204
+ error: `Missing required public key field "${field}" for kty="${kty}"`
205
+ };
206
+ }
207
+ }
208
+ }
209
+ return { valid: true };
210
+ }
211
+ function canonicalizeJwkJson(jwk) {
212
+ const sorted = Object.keys(jwk).sort();
213
+ const obj = {};
214
+ for (const key of sorted) {
215
+ obj[key] = jwk[key];
216
+ }
217
+ return JSON.stringify(obj);
218
+ }
219
+ function jwkToDidJwk(jwk) {
220
+ assertObject(jwk, "jwk", "INVALID_JWK");
221
+ const validation = validatePublicJwk(jwk);
222
+ if (!validation.valid) {
223
+ throw new OmaTrustError("INVALID_JWK", validation.error ?? "Invalid public JWK", { jwk });
224
+ }
225
+ const canonical = canonicalizeJwkJson(jwk);
226
+ const encoded = base64url.encode(new TextEncoder().encode(canonical));
227
+ return `did:jwk:${encoded}`;
228
+ }
229
+ function didJwkToJwk(didJwk) {
230
+ if (typeof didJwk !== "string" || !didJwk.startsWith("did:jwk:")) {
231
+ throw new OmaTrustError("INVALID_DID", "Expected a did:jwk DID", { input: didJwk });
232
+ }
233
+ const parts = didJwk.split(":");
234
+ if (parts.length !== 3) {
235
+ throw new OmaTrustError("INVALID_DID", "did:jwk must have exactly 3 colon-separated parts", {
236
+ input: didJwk
237
+ });
238
+ }
239
+ const encoded = parts[2];
240
+ if (!encoded || encoded.length === 0) {
241
+ throw new OmaTrustError("INVALID_DID", "Missing base64url-encoded JWK identifier", {
242
+ input: didJwk
243
+ });
244
+ }
245
+ let decoded;
246
+ try {
247
+ const bytes = base64url.decode(encoded);
248
+ decoded = new TextDecoder().decode(bytes);
249
+ } catch {
250
+ throw new OmaTrustError("INVALID_DID", "Failed to base64url-decode did:jwk identifier", {
251
+ input: didJwk
252
+ });
253
+ }
254
+ let jwk;
255
+ try {
256
+ jwk = JSON.parse(decoded);
257
+ } catch {
258
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk identifier is not valid JSON", {
259
+ input: didJwk
260
+ });
261
+ }
262
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
263
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk must be a JSON object", {
264
+ input: didJwk
265
+ });
266
+ }
267
+ const validation = validatePublicJwk(jwk);
268
+ if (!validation.valid) {
269
+ throw new OmaTrustError("INVALID_DID", validation.error ?? "Invalid public JWK in did:jwk", {
270
+ input: didJwk
271
+ });
272
+ }
273
+ return jwk;
274
+ }
275
+ function extractPublicKeyFields(jwk) {
276
+ const result = {};
277
+ const metadataFields = /* @__PURE__ */ new Set(["kid", "use", "key_ops", "alg", "ext"]);
278
+ for (const [key, value] of Object.entries(jwk)) {
279
+ if (PRIVATE_KEY_FIELDS.has(key)) continue;
280
+ if (metadataFields.has(key)) continue;
281
+ result[key] = value;
282
+ }
283
+ return result;
284
+ }
285
+ function publicJwkEquals(a, b) {
286
+ assertObject(a, "a", "INVALID_JWK");
287
+ assertObject(b, "b", "INVALID_JWK");
288
+ const aObj = a;
289
+ const bObj = b;
290
+ for (const field of PRIVATE_KEY_FIELDS) {
291
+ if (field in aObj) {
292
+ throw new OmaTrustError(
293
+ "INVALID_JWK",
294
+ `First JWK contains private key field "${field}"`,
295
+ { field }
296
+ );
297
+ }
298
+ if (field in bObj) {
299
+ throw new OmaTrustError(
300
+ "INVALID_JWK",
301
+ `Second JWK contains private key field "${field}"`,
302
+ { field }
303
+ );
304
+ }
305
+ }
306
+ const aPublic = extractPublicKeyFields(aObj);
307
+ const bPublic = extractPublicKeyFields(bObj);
308
+ return canonicalizeJwkJson(aPublic) === canonicalizeJwkJson(bPublic);
309
+ }
310
+ async function computeJwkThumbprint(jwk, digestAlgorithm = "sha256") {
311
+ assertObject(jwk, "jwk", "INVALID_JWK");
312
+ const validation = validatePublicJwk(jwk);
313
+ if (!validation.valid) {
314
+ throw new OmaTrustError("INVALID_JWK", validation.error ?? "Invalid public JWK", { jwk });
315
+ }
316
+ const alg = digestAlgorithm === "sha256" ? "sha256" : digestAlgorithm === "sha384" ? "sha384" : "sha512";
317
+ return calculateJwkThumbprint(jwk, alg);
318
+ }
319
+ async function formatJktValue(jwk) {
320
+ const thumbprint = await computeJwkThumbprint(jwk, "sha256");
321
+ return `jkt=S256:${thumbprint}`;
322
+ }
139
323
 
140
324
  // src/identity/did.ts
141
325
  var DID_REGEX = /^did:[a-z0-9]+:.+$/i;
@@ -211,7 +395,7 @@ function normalizeDidKey(input) {
211
395
  }
212
396
  function normalizeDid(input) {
213
397
  assertString(input, "input", "INVALID_DID");
214
- const trimmed = input.trim();
398
+ const trimmed = input.trim().split("#")[0];
215
399
  if (!trimmed.startsWith("did:")) {
216
400
  return normalizeDidWeb(trimmed);
217
401
  }
@@ -228,6 +412,8 @@ function normalizeDid(input) {
228
412
  return normalizeDidHandle(trimmed);
229
413
  case "key":
230
414
  return normalizeDidKey(trimmed);
415
+ case "jwk":
416
+ return normalizeDidJwk(trimmed);
231
417
  default:
232
418
  return trimmed;
233
419
  }
@@ -331,29 +517,843 @@ function extractAddressFromDid(identifier) {
331
517
  }
332
518
  throw new OmaTrustError("INVALID_DID", "Unsupported identifier format", { identifier });
333
519
  }
520
+ var CAIP2_NAMESPACE_REGEX = /^[a-z0-9-]{3,8}$/;
521
+ var BASE64URL_REGEX = /^[A-Za-z0-9_-]+$/;
522
+ var VALID_JWK_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
523
+ function isPrivateKeyDid(did) {
524
+ return validatePrivateKeyDid(did).valid;
525
+ }
526
+ function validatePrivateKeyDid(did) {
527
+ if (typeof did !== "string" || did.trim().length === 0) {
528
+ return { valid: false, method: null, error: "DID must be a non-empty string" };
529
+ }
530
+ const trimmed = did.trim();
531
+ const method = extractDidMethod(trimmed);
532
+ switch (method) {
533
+ case "pkh":
534
+ return validateDidPkh(trimmed);
535
+ case "jwk":
536
+ return validateDidJwk(trimmed);
537
+ default:
538
+ return {
539
+ valid: false,
540
+ method: null,
541
+ error: method ? `DID method "${method}" is not a recognized private-key method` : "Invalid DID format"
542
+ };
543
+ }
544
+ }
545
+ function validateDidPkh(did) {
546
+ const parts = did.split(":");
547
+ if (parts.length !== 5) {
548
+ return {
549
+ valid: false,
550
+ method: "pkh",
551
+ error: `did:pkh must have exactly 5 colon-separated parts, got ${parts.length}`
552
+ };
553
+ }
554
+ const [, , namespace, chainId, address] = parts;
555
+ if (!namespace) {
556
+ return { valid: false, method: "pkh", error: "Missing namespace" };
557
+ }
558
+ if (!CAIP2_NAMESPACE_REGEX.test(namespace)) {
559
+ return {
560
+ valid: false,
561
+ method: "pkh",
562
+ error: `Invalid CAIP-2 namespace "${namespace}" (must be 3-8 lowercase alphanumeric/hyphen chars)`
563
+ };
564
+ }
565
+ if (!chainId) {
566
+ return { valid: false, method: "pkh", error: "Missing chain ID (reference)" };
567
+ }
568
+ if (!address) {
569
+ return { valid: false, method: "pkh", error: "Missing address" };
570
+ }
571
+ if (namespace === "eip155") {
572
+ if (!/^\d+$/.test(chainId)) {
573
+ return {
574
+ valid: false,
575
+ method: "pkh",
576
+ error: `Invalid eip155 chain ID "${chainId}" (must be numeric)`
577
+ };
578
+ }
579
+ if (!isAddress(address)) {
580
+ return {
581
+ valid: false,
582
+ method: "pkh",
583
+ error: `Invalid EVM address "${address}" (must be 0x + 40 hex chars)`
584
+ };
585
+ }
586
+ }
587
+ if (namespace === "solana") {
588
+ if (!/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address)) {
589
+ return {
590
+ valid: false,
591
+ method: "pkh",
592
+ error: `Invalid Solana address "${address}" (must be 32-44 base58 characters)`
593
+ };
594
+ }
595
+ }
596
+ return { valid: true, method: "pkh" };
597
+ }
598
+ function validateDidJwk(did) {
599
+ const parts = did.split(":");
600
+ if (parts.length !== 3) {
601
+ return {
602
+ valid: false,
603
+ method: "jwk",
604
+ error: `did:jwk must have exactly 3 colon-separated parts, got ${parts.length}`
605
+ };
606
+ }
607
+ const [, , encoded] = parts;
608
+ if (!encoded || encoded.length === 0) {
609
+ return { valid: false, method: "jwk", error: "Missing base64url-encoded JWK identifier" };
610
+ }
611
+ if (!BASE64URL_REGEX.test(encoded)) {
612
+ return {
613
+ valid: false,
614
+ method: "jwk",
615
+ error: "Identifier contains invalid base64url characters"
616
+ };
617
+ }
618
+ let decoded;
619
+ try {
620
+ const bytes = base64url.decode(encoded);
621
+ decoded = new TextDecoder().decode(bytes);
622
+ } catch {
623
+ return { valid: false, method: "jwk", error: "Failed to base64url-decode identifier" };
624
+ }
625
+ let jwk;
626
+ try {
627
+ jwk = JSON.parse(decoded);
628
+ } catch {
629
+ return { valid: false, method: "jwk", error: "Decoded identifier is not valid JSON" };
630
+ }
631
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
632
+ return { valid: false, method: "jwk", error: "Decoded JWK must be a JSON object" };
633
+ }
634
+ const kty = jwk.kty;
635
+ if (typeof kty !== "string" || !VALID_JWK_KTY.has(kty)) {
636
+ return {
637
+ valid: false,
638
+ method: "jwk",
639
+ error: `Invalid or missing kty field (must be one of: EC, OKP, RSA)`
640
+ };
641
+ }
642
+ if ("d" in jwk) {
643
+ return {
644
+ valid: false,
645
+ method: "jwk",
646
+ error: "DID must reference a public key \u2014 private key component (d) is not allowed"
647
+ };
648
+ }
649
+ const jwkValidation = validatePublicJwk(jwk);
650
+ if (!jwkValidation.valid) {
651
+ return { valid: false, method: "jwk", error: jwkValidation.error };
652
+ }
653
+ return { valid: true, method: "jwk" };
654
+ }
655
+ function normalizeDidJwk(input) {
656
+ assertString(input, "input", "INVALID_DID");
657
+ const trimmed = input.trim();
658
+ if (!trimmed.startsWith("did:jwk:")) {
659
+ throw new OmaTrustError("INVALID_DID", "Expected did:jwk DID", { input });
660
+ }
661
+ const result = validateDidJwk(trimmed);
662
+ if (!result.valid) {
663
+ throw new OmaTrustError("INVALID_DID", result.error ?? "Invalid did:jwk", { input });
664
+ }
665
+ return trimmed;
666
+ }
667
+
668
+ // src/identity/did-url.ts
669
+ var DID_URL_REGEX = /^did:[a-z0-9]+:.+$/i;
670
+ function parseDidUrl(input) {
671
+ assertString(input, "input", "INVALID_DID_URL");
672
+ const trimmed = input.trim();
673
+ const hashIndex = trimmed.indexOf("#");
674
+ let did;
675
+ let fragment;
676
+ if (hashIndex === -1) {
677
+ did = trimmed;
678
+ fragment = null;
679
+ } else {
680
+ did = trimmed.slice(0, hashIndex);
681
+ const rawFragment = trimmed.slice(hashIndex + 1);
682
+ if (rawFragment.length === 0) {
683
+ throw new OmaTrustError(
684
+ "INVALID_DID_URL",
685
+ "DID URL has empty fragment (trailing '#' with no identifier)",
686
+ { input }
687
+ );
688
+ }
689
+ fragment = rawFragment;
690
+ }
691
+ if (!DID_URL_REGEX.test(did)) {
692
+ throw new OmaTrustError(
693
+ "INVALID_DID_URL",
694
+ "Invalid DID URL: base DID portion is malformed",
695
+ { input, did }
696
+ );
697
+ }
698
+ return {
699
+ didUrl: trimmed,
700
+ did,
701
+ fragment
702
+ };
703
+ }
704
+ function isDidUrl(input) {
705
+ if (typeof input !== "string") return false;
706
+ const trimmed = input.trim();
707
+ return trimmed.includes("#") && DID_URL_REGEX.test(trimmed.split("#")[0]);
708
+ }
709
+ function assertBareDid(input, paramName = "did") {
710
+ assertString(input, paramName, "INVALID_DID");
711
+ if (isDidUrl(input)) {
712
+ throw new OmaTrustError(
713
+ "INVALID_DID",
714
+ `Expected a bare DID but received a DID URL with a fragment. Use parseDidUrl() to handle DID URLs.`,
715
+ { input }
716
+ );
717
+ }
718
+ }
719
+
720
+ // src/shared/did-document.ts
721
+ async function fetchDidDocument(domain) {
722
+ const normalized = domain.toLowerCase().replace(/\.$/, "");
723
+ const url = `https://${normalized}/.well-known/did.json`;
724
+ let response;
725
+ try {
726
+ response = await fetch(url, { headers: { Accept: "application/json" } });
727
+ } catch (err) {
728
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch DID document", { domain, err });
729
+ }
730
+ if (!response.ok) {
731
+ throw new OmaTrustError("NETWORK_ERROR", `DID document fetch failed: ${response.status}`, {
732
+ domain,
733
+ status: response.status
734
+ });
735
+ }
736
+ return await response.json();
737
+ }
738
+
739
+ // src/identity/resolve-key.ts
740
+ function findVerificationMethod(didDocument, didUrl, fragment) {
741
+ const methods = didDocument.verificationMethod;
742
+ if (!Array.isArray(methods)) {
743
+ return null;
744
+ }
745
+ for (const method of methods) {
746
+ if (!method || typeof method !== "object") continue;
747
+ const id2 = method.id;
748
+ if (typeof id2 !== "string") continue;
749
+ if (id2 === didUrl) return method;
750
+ if (fragment && (id2 === `#${fragment}` || id2.endsWith(`#${fragment}`))) {
751
+ return method;
752
+ }
753
+ }
754
+ return null;
755
+ }
756
+ function extractMethodIdsFromDidDocument(didDocument) {
757
+ const methods = didDocument.verificationMethod;
758
+ if (!Array.isArray(methods)) return [];
759
+ return methods.filter((m) => m && typeof m.id === "string").map((m) => m.id);
760
+ }
761
+ async function resolveDidUrlToPublicKey(didUrl, options) {
762
+ assertString(didUrl, "didUrl", "INVALID_DID_URL");
763
+ const parsed = parseDidUrl(didUrl);
764
+ const method = extractDidMethod(parsed.did);
765
+ if (!method) {
766
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract DID method from DID URL", {
767
+ didUrl,
768
+ did: parsed.did
769
+ });
770
+ }
771
+ switch (method) {
772
+ case "web":
773
+ return resolveDidUrlKey(parsed.didUrl, parsed.did, parsed.fragment, options);
774
+ default:
775
+ throw new OmaTrustError(
776
+ "UNSUPPORTED_DID_METHOD",
777
+ `DID URL key resolution is not supported for method "${method}"`,
778
+ { didUrl, method }
779
+ );
780
+ }
781
+ }
782
+ async function resolveDidUrlToControllerDid(didUrl, options) {
783
+ const resolved = await resolveDidUrlToPublicKey(didUrl, options);
784
+ const controllerDid = jwkToDidJwk(resolved.publicKeyJwk);
785
+ return {
786
+ ...resolved,
787
+ controllerDid
788
+ };
789
+ }
790
+ async function resolveDidUrlKey(didUrl, did, fragment, options) {
791
+ const domain = getDomainFromDidWeb(did);
792
+ if (!domain) {
793
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract domain from did:web DID", {
794
+ didUrl,
795
+ did
796
+ });
797
+ }
798
+ const fetchFn = options?.fetchDidDocument ?? fetchDidDocument;
799
+ const didDocument = await fetchFn(domain);
800
+ const method = findVerificationMethod(didDocument, didUrl, fragment);
801
+ if (!method) {
802
+ throw new OmaTrustError(
803
+ "KEY_NOT_FOUND",
804
+ `No verification method found matching "${fragment ? `#${fragment}` : didUrl}"`,
805
+ { didUrl, fragment, availableMethods: extractMethodIdsFromDidDocument(didDocument) }
806
+ );
807
+ }
808
+ const publicKeyJwk = method.publicKeyJwk;
809
+ if (!publicKeyJwk || typeof publicKeyJwk !== "object") {
810
+ throw new OmaTrustError(
811
+ "KEY_NOT_FOUND",
812
+ `Verification method "${method.id}" does not contain publicKeyJwk`,
813
+ { didUrl, verificationMethodId: method.id }
814
+ );
815
+ }
816
+ const validation = validatePublicJwk(publicKeyJwk);
817
+ if (!validation.valid) {
818
+ throw new OmaTrustError(
819
+ "INVALID_JWK",
820
+ `publicKeyJwk in verification method "${method.id}" is invalid: ${validation.error}`,
821
+ { didUrl, verificationMethodId: method.id }
822
+ );
823
+ }
824
+ return {
825
+ didUrl,
826
+ did,
827
+ fragment,
828
+ publicKeyJwk,
829
+ verificationMethodId: method.id
830
+ };
831
+ }
832
+
833
+ // src/identity/controller-id.ts
834
+ function isSameControllerId(a, b) {
835
+ let normalizedA = null;
836
+ let normalizedB = null;
837
+ try {
838
+ normalizedA = normalizeDid(a);
839
+ } catch {
840
+ }
841
+ try {
842
+ normalizedB = normalizeDid(b);
843
+ } catch {
844
+ }
845
+ if (normalizedA && normalizedB && normalizedA === normalizedB) {
846
+ return true;
847
+ }
848
+ const evmA = extractControllerEvmAddress(a);
849
+ const evmB = extractControllerEvmAddress(b);
850
+ if (evmA && evmB && evmA.toLowerCase() === evmB.toLowerCase()) {
851
+ return true;
852
+ }
853
+ const methodA = extractDidMethod(a);
854
+ const methodB = extractDidMethod(b);
855
+ if (methodA === "jwk" && methodB === "jwk") {
856
+ try {
857
+ const jwkA = didJwkToJwk(a);
858
+ const jwkB = didJwkToJwk(b);
859
+ return publicJwkEquals(jwkA, jwkB);
860
+ } catch {
861
+ return false;
862
+ }
863
+ }
864
+ return false;
865
+ }
866
+ function extractControllerEvmAddress(controllerDid) {
867
+ try {
868
+ const method = extractDidMethod(controllerDid);
869
+ if (method === "pkh" && controllerDid.includes("eip155")) {
870
+ return extractAddressFromDid(controllerDid);
871
+ }
872
+ } catch {
873
+ }
874
+ return null;
875
+ }
876
+
877
+ // src/identity/types.ts
878
+ function extractAuthorizationMetadata(result) {
879
+ const payload = result.payload;
880
+ const resourceUrl = typeof payload.resourceUrl === "string" ? payload.resourceUrl : null;
881
+ const issuedAt = typeof payload.issuedAt === "string" ? payload.issuedAt : typeof payload.issuedAt === "number" ? payload.issuedAt : null;
882
+ let subjectDid = null;
883
+ if (resourceUrl) {
884
+ try {
885
+ const url = new URL(resourceUrl);
886
+ subjectDid = `did:web:${url.hostname}`;
887
+ } catch {
888
+ }
889
+ }
890
+ if ("publicKeyDid" in result) {
891
+ const jwsResult = result;
892
+ return {
893
+ controllerDid: jwsResult.publicKeyDid,
894
+ subjectDid,
895
+ resourceUrl,
896
+ issuedAt,
897
+ kid: jwsResult.kid,
898
+ publicKeyJwk: jwsResult.publicKeyJwk,
899
+ signer: null
900
+ };
901
+ }
902
+ const eip712Result = result;
903
+ const signerAddress = eip712Result.signer.toLowerCase();
904
+ return {
905
+ controllerDid: `did:pkh:eip155:1:${signerAddress}`,
906
+ subjectDid,
907
+ resourceUrl,
908
+ issuedAt,
909
+ kid: null,
910
+ publicKeyJwk: null,
911
+ signer: eip712Result.signer
912
+ };
913
+ }
914
+ var VALID_ALGORITHMS = /* @__PURE__ */ new Set(["keccak256", "sha256"]);
915
+ var MAX_JSON_DEPTH = 32;
916
+ function assertDepthLimit(depth, context) {
917
+ if (depth > MAX_JSON_DEPTH) {
918
+ const msg = context ? `JSON nesting depth exceeds maximum of ${MAX_JSON_DEPTH} at ${context}` : `JSON nesting depth exceeds maximum of ${MAX_JSON_DEPTH}`;
919
+ throw new OmaTrustError("INVALID_INPUT", msg);
920
+ }
921
+ }
922
+ function parseJsonStrict(input) {
923
+ if (typeof input !== "string") {
924
+ throw new OmaTrustError("INVALID_INPUT", "Input must be a string");
925
+ }
926
+ const duplicateKeys = detectDuplicateKeys(input);
927
+ if (duplicateKeys.length > 0) {
928
+ throw new OmaTrustError(
929
+ "INVALID_INPUT",
930
+ `Duplicate JSON key${duplicateKeys.length > 1 ? "s" : ""} detected: ${duplicateKeys.map((k) => `"${k}"`).join(", ")}`,
931
+ { duplicateKeys }
932
+ );
933
+ }
934
+ try {
935
+ return JSON.parse(input);
936
+ } catch {
937
+ throw new OmaTrustError("INVALID_INPUT", "Input is not valid JSON");
938
+ }
939
+ }
940
+ function detectDuplicateKeys(input) {
941
+ const duplicates = [];
942
+ const objectKeyStack = [];
943
+ let inString = false;
944
+ let escaped = false;
945
+ let currentKey = "";
946
+ let collectingKey = false;
947
+ let afterColon = false;
948
+ let i = 0;
949
+ while (i < input.length) {
950
+ const ch = input[i];
951
+ if (escaped) {
952
+ if (collectingKey) currentKey += ch;
953
+ escaped = false;
954
+ i++;
955
+ continue;
956
+ }
957
+ if (ch === "\\") {
958
+ escaped = true;
959
+ if (collectingKey) currentKey += ch;
960
+ i++;
961
+ continue;
962
+ }
963
+ if (ch === '"') {
964
+ if (!inString) {
965
+ inString = true;
966
+ if (objectKeyStack.length > 0 && !afterColon) {
967
+ collectingKey = true;
968
+ currentKey = "";
969
+ }
970
+ } else {
971
+ inString = false;
972
+ if (collectingKey) {
973
+ collectingKey = false;
974
+ const keySet = objectKeyStack[objectKeyStack.length - 1];
975
+ if (keySet.has(currentKey)) {
976
+ if (!duplicates.includes(currentKey)) {
977
+ duplicates.push(currentKey);
978
+ }
979
+ } else {
980
+ keySet.add(currentKey);
981
+ }
982
+ }
983
+ }
984
+ i++;
985
+ continue;
986
+ }
987
+ if (inString) {
988
+ if (collectingKey) currentKey += ch;
989
+ i++;
990
+ continue;
991
+ }
992
+ if (ch === "{") {
993
+ objectKeyStack.push(/* @__PURE__ */ new Set());
994
+ assertDepthLimit(objectKeyStack.length);
995
+ afterColon = false;
996
+ } else if (ch === "}") {
997
+ objectKeyStack.pop();
998
+ afterColon = objectKeyStack.length > 0;
999
+ } else if (ch === "[") {
1000
+ objectKeyStack.push(/* @__PURE__ */ new Set(["__array__"]));
1001
+ assertDepthLimit(objectKeyStack.length);
1002
+ afterColon = false;
1003
+ } else if (ch === "]") {
1004
+ objectKeyStack.pop();
1005
+ afterColon = objectKeyStack.length > 0;
1006
+ } else if (ch === ":") {
1007
+ afterColon = true;
1008
+ } else if (ch === ",") {
1009
+ afterColon = false;
1010
+ }
1011
+ i++;
1012
+ }
1013
+ return duplicates;
1014
+ }
1015
+ function assertJsonSafe(value, path = "$", depth = 0) {
1016
+ assertDepthLimit(depth, path);
1017
+ if (value === void 0) {
1018
+ throw new OmaTrustError("INVALID_INPUT", `Non-JSON-safe value at ${path}: undefined`);
1019
+ }
1020
+ if (value === null) return;
1021
+ switch (typeof value) {
1022
+ case "string":
1023
+ case "boolean":
1024
+ return;
1025
+ case "number":
1026
+ if (!Number.isFinite(value)) {
1027
+ throw new OmaTrustError("INVALID_INPUT", `Non-JSON-safe value at ${path}: ${value} (must be finite number)`);
1028
+ }
1029
+ return;
1030
+ case "bigint":
1031
+ throw new OmaTrustError("INVALID_INPUT", `Non-JSON-safe value at ${path}: BigInt (use number or string)`);
1032
+ case "function":
1033
+ throw new OmaTrustError("INVALID_INPUT", `Non-JSON-safe value at ${path}: function`);
1034
+ case "symbol":
1035
+ throw new OmaTrustError("INVALID_INPUT", `Non-JSON-safe value at ${path}: Symbol`);
1036
+ case "object":
1037
+ if (value instanceof Date) {
1038
+ throw new OmaTrustError("INVALID_INPUT", `Non-JSON-safe value at ${path}: Date (use ISO string or timestamp)`);
1039
+ }
1040
+ if (value instanceof RegExp) {
1041
+ throw new OmaTrustError("INVALID_INPUT", `Non-JSON-safe value at ${path}: RegExp`);
1042
+ }
1043
+ if (Array.isArray(value)) {
1044
+ for (let i = 0; i < value.length; i++) {
1045
+ assertJsonSafe(value[i], `${path}[${i}]`, depth + 1);
1046
+ }
1047
+ return;
1048
+ }
1049
+ for (const [key, val] of Object.entries(value)) {
1050
+ assertJsonSafe(val, `${path}.${key}`, depth + 1);
1051
+ }
1052
+ return;
1053
+ default:
1054
+ throw new OmaTrustError("INVALID_INPUT", `Non-JSON-safe value at ${path}: unknown type "${typeof value}"`);
1055
+ }
1056
+ }
334
1057
  function canonicalizeJson(obj) {
1058
+ assertJsonSafe(obj);
335
1059
  const jcs = canonicalize(obj);
336
1060
  if (!jcs) {
337
1061
  throw new OmaTrustError("INVALID_INPUT", "Object cannot be canonicalized", { obj });
338
1062
  }
339
1063
  return jcs;
340
1064
  }
341
- function canonicalizeForHash(obj) {
1065
+ function canonicalizeAndKeccak256(obj) {
342
1066
  const jcsJson = canonicalizeJson(obj);
343
1067
  return {
344
1068
  jcsJson,
345
1069
  hash: keccak256(toUtf8Bytes(jcsJson))
346
1070
  };
347
1071
  }
1072
+ function canonicalizeForHash(obj) {
1073
+ return canonicalizeAndKeccak256(obj);
1074
+ }
348
1075
  function hashCanonicalizedJson(obj, algorithm) {
1076
+ if (!VALID_ALGORITHMS.has(algorithm)) {
1077
+ throw new OmaTrustError("INVALID_INPUT", `Unsupported hash algorithm: "${algorithm}". Must be "keccak256" or "sha256".`, { algorithm });
1078
+ }
349
1079
  const jcs = canonicalizeJson(obj);
350
1080
  const bytes = toUtf8Bytes(jcs);
351
1081
  return algorithm === "keccak256" ? keccak256(bytes) : sha256(bytes);
352
1082
  }
1083
+ var DID_ARTIFACT_PREFIX = "did:artifact:";
1084
+ var CID_VERSION = 1;
1085
+ var RAW_CODEC = raw.code;
1086
+ var SHA2_256_CODE = 18;
1087
+ var SHA2_256_DIGEST_LENGTH = 32;
1088
+ async function artifactDidFromBytes(bytes) {
1089
+ if (!(bytes instanceof Uint8Array) || bytes.length === 0) {
1090
+ throw new OmaTrustError(
1091
+ "INVALID_INPUT",
1092
+ "bytes must be a non-empty Uint8Array"
1093
+ );
1094
+ }
1095
+ const digest = await sha256$1.digest(bytes);
1096
+ const cid = CID.createV1(RAW_CODEC, digest);
1097
+ return `${DID_ARTIFACT_PREFIX}${cid.toString(base32)}`;
1098
+ }
1099
+ async function artifactDidFromJson(input) {
1100
+ const value = typeof input === "string" ? parseJsonStrict(input) : input;
1101
+ const jcs = canonicalizeJson(value);
1102
+ const bytes = new TextEncoder().encode(jcs);
1103
+ const digest = await sha256$1.digest(bytes);
1104
+ const cid = CID.createV1(RAW_CODEC, digest);
1105
+ return `${DID_ARTIFACT_PREFIX}${cid.toString(base32)}`;
1106
+ }
1107
+ function parseArtifactDid(did) {
1108
+ if (typeof did !== "string" || !did.startsWith(DID_ARTIFACT_PREFIX)) {
1109
+ throw new OmaTrustError("INVALID_DID", "Expected a did:artifact DID", { did });
1110
+ }
1111
+ const identifier = did.slice(DID_ARTIFACT_PREFIX.length);
1112
+ if (!identifier || identifier.length === 0) {
1113
+ throw new OmaTrustError("INVALID_DID", "Missing method-specific identifier", { did });
1114
+ }
1115
+ if (identifier[0] !== "b") {
1116
+ throw new OmaTrustError(
1117
+ "INVALID_DID",
1118
+ `Invalid multibase prefix "${identifier[0]}", expected "b" (base32lower)`,
1119
+ { did }
1120
+ );
1121
+ }
1122
+ let cid;
1123
+ try {
1124
+ cid = CID.parse(identifier, base32);
1125
+ } catch (err) {
1126
+ throw new OmaTrustError(
1127
+ "INVALID_DID",
1128
+ "Failed to decode base32 CID from did:artifact identifier",
1129
+ { did, cause: err }
1130
+ );
1131
+ }
1132
+ if (cid.version !== CID_VERSION) {
1133
+ throw new OmaTrustError(
1134
+ "INVALID_DID",
1135
+ `CID version must be 1, got ${cid.version}`,
1136
+ { did }
1137
+ );
1138
+ }
1139
+ if (cid.code !== RAW_CODEC) {
1140
+ throw new OmaTrustError(
1141
+ "INVALID_DID",
1142
+ `Multicodec must be raw (0x55), got 0x${cid.code.toString(16)}`,
1143
+ { did }
1144
+ );
1145
+ }
1146
+ if (cid.multihash.code !== SHA2_256_CODE) {
1147
+ throw new OmaTrustError(
1148
+ "INVALID_DID",
1149
+ `Multihash function must be sha2-256 (0x12), got 0x${cid.multihash.code.toString(16)}`,
1150
+ { did }
1151
+ );
1152
+ }
1153
+ if (cid.multihash.digest.length !== SHA2_256_DIGEST_LENGTH) {
1154
+ throw new OmaTrustError(
1155
+ "INVALID_DID",
1156
+ `SHA-256 digest must be 32 bytes, got ${cid.multihash.digest.length}`,
1157
+ { did }
1158
+ );
1159
+ }
1160
+ const digest = cid.multihash.digest;
1161
+ const digestHex = Array.from(digest).map((b) => b.toString(16).padStart(2, "0")).join("");
1162
+ return { did, identifier, digest, digestHex };
1163
+ }
1164
+ async function verifyDidArtifact(did, content) {
1165
+ let parsed;
1166
+ try {
1167
+ parsed = parseArtifactDid(did);
1168
+ } catch (err) {
1169
+ return {
1170
+ valid: false,
1171
+ reason: err instanceof OmaTrustError ? err.message : "Invalid did:artifact DID"
1172
+ };
1173
+ }
1174
+ const expectedHex = parsed.digestHex;
1175
+ if (typeof content === "string" || typeof content === "object" && content !== null && !(content instanceof Uint8Array)) {
1176
+ try {
1177
+ const value = typeof content === "string" ? parseJsonStrict(content) : content;
1178
+ const jcs = canonicalizeJson(value);
1179
+ const jcsBytes = new TextEncoder().encode(jcs);
1180
+ const digest2 = await sha256$1.digest(jcsBytes);
1181
+ const digestHex2 = Array.from(digest2.digest).map((b) => b.toString(16).padStart(2, "0")).join("");
1182
+ if (digestHex2 === expectedHex) {
1183
+ return { valid: true, matchedAs: "json" };
1184
+ }
1185
+ } catch {
1186
+ }
1187
+ }
1188
+ let rawBytes;
1189
+ if (content instanceof Uint8Array) {
1190
+ rawBytes = content;
1191
+ } else if (typeof content === "string") {
1192
+ rawBytes = new TextEncoder().encode(content);
1193
+ } else {
1194
+ return {
1195
+ valid: false,
1196
+ reason: "Content did not match as canonical JSON and is not raw bytes"
1197
+ };
1198
+ }
1199
+ const digest = await sha256$1.digest(rawBytes);
1200
+ const digestHex = Array.from(digest.digest).map((b) => b.toString(16).padStart(2, "0")).join("");
1201
+ if (digestHex === expectedHex) {
1202
+ return { valid: true, matchedAs: "binary" };
1203
+ }
1204
+ return {
1205
+ valid: false,
1206
+ reason: "Content does not match the did:artifact identifier (neither as canonical JSON nor as raw bytes)"
1207
+ };
1208
+ }
1209
+ function didEthrToDidPkh(did) {
1210
+ if (typeof did !== "string" || !did.startsWith("did:ethr:")) {
1211
+ throw new OmaTrustError("INVALID_DID", "Expected a did:ethr DID", { did });
1212
+ }
1213
+ const remainder = did.slice("did:ethr:".length);
1214
+ if (!remainder) {
1215
+ throw new OmaTrustError("INVALID_DID", "Missing identifier in did:ethr DID", { did });
1216
+ }
1217
+ let chainId;
1218
+ let address;
1219
+ const parts = remainder.split(":");
1220
+ if (parts.length === 1) {
1221
+ chainId = "1";
1222
+ address = parts[0];
1223
+ } else if (parts.length === 2) {
1224
+ const rawChainId = parts[0];
1225
+ address = parts[1];
1226
+ if (rawChainId.startsWith("0x")) {
1227
+ chainId = String(parseInt(rawChainId, 16));
1228
+ } else if (/^\d+$/.test(rawChainId)) {
1229
+ chainId = rawChainId;
1230
+ } else {
1231
+ const networkMap = {
1232
+ mainnet: "1",
1233
+ goerli: "5",
1234
+ sepolia: "11155111",
1235
+ polygon: "137",
1236
+ arbitrum: "42161",
1237
+ optimism: "10",
1238
+ base: "8453"
1239
+ };
1240
+ const mapped = networkMap[rawChainId.toLowerCase()];
1241
+ if (!mapped) {
1242
+ throw new OmaTrustError(
1243
+ "INVALID_DID",
1244
+ `Unknown did:ethr network "${rawChainId}"`,
1245
+ { did, network: rawChainId }
1246
+ );
1247
+ }
1248
+ chainId = mapped;
1249
+ }
1250
+ } else {
1251
+ throw new OmaTrustError("INVALID_DID", "Invalid did:ethr format", { did });
1252
+ }
1253
+ if (!isAddress(address)) {
1254
+ throw new OmaTrustError("INVALID_DID", "Invalid Ethereum address in did:ethr DID", {
1255
+ did,
1256
+ address
1257
+ });
1258
+ }
1259
+ const checksummed = getAddress(address);
1260
+ return `did:pkh:eip155:${chainId}:${checksummed.toLowerCase()}`;
1261
+ }
1262
+ var MULTICODEC_KEY_TYPES = {
1263
+ // Ed25519 public key: 0xed (varint encoded as [0xed, 0x01])
1264
+ 237: { kty: "OKP", crv: "Ed25519" },
1265
+ // X25519 public key: 0xec (varint encoded as [0xec, 0x01])
1266
+ 236: { kty: "OKP", crv: "X25519" },
1267
+ // secp256k1 public key (compressed): 0xe7 (varint encoded as [0xe7, 0x01])
1268
+ 231: { kty: "EC", crv: "secp256k1", coordSize: 33 },
1269
+ // P-256 public key (compressed): 0x80 0x24 (varint 0x1200)
1270
+ 4608: { kty: "EC", crv: "P-256", coordSize: 33 },
1271
+ // P-384 public key (compressed): 0x81 0x24 (varint 0x1201)
1272
+ 4609: { kty: "EC", crv: "P-384", coordSize: 49 }
1273
+ };
1274
+ function readVarint(bytes, offset) {
1275
+ let value = 0;
1276
+ let shift = 0;
1277
+ let bytesRead = 0;
1278
+ while (offset + bytesRead < bytes.length) {
1279
+ const byte = bytes[offset + bytesRead];
1280
+ value |= (byte & 127) << shift;
1281
+ bytesRead++;
1282
+ if ((byte & 128) === 0) {
1283
+ return { value, bytesRead };
1284
+ }
1285
+ shift += 7;
1286
+ if (shift > 28) {
1287
+ throw new OmaTrustError("INVALID_DID", "Varint too long in did:key multicodec prefix");
1288
+ }
1289
+ }
1290
+ throw new OmaTrustError("INVALID_DID", "Truncated varint in did:key multicodec prefix");
1291
+ }
1292
+ function decompressEcKey(compressedKey, crv) {
1293
+ throw new OmaTrustError(
1294
+ "UNSUPPORTED_KEY_TYPE",
1295
+ `Cannot convert compressed EC key (${crv}) from did:key to did:jwk. Decompressing EC points requires elliptic curve arithmetic. For EVM keys, use didEthrToDidPkh() instead.`,
1296
+ { crv, keyLength: compressedKey.length }
1297
+ );
1298
+ }
1299
+ function didKeyToDidJwk(did) {
1300
+ if (typeof did !== "string" || !did.startsWith("did:key:")) {
1301
+ throw new OmaTrustError("INVALID_DID", "Expected a did:key DID", { did });
1302
+ }
1303
+ const identifier = did.slice("did:key:".length);
1304
+ if (!identifier || !identifier.startsWith("z")) {
1305
+ throw new OmaTrustError(
1306
+ "INVALID_DID",
1307
+ "did:key identifier must start with 'z' (base58btc multibase prefix)",
1308
+ { did }
1309
+ );
1310
+ }
1311
+ let decoded;
1312
+ try {
1313
+ decoded = base58btc.decode(identifier);
1314
+ } catch (err) {
1315
+ throw new OmaTrustError(
1316
+ "INVALID_DID",
1317
+ "Failed to decode base58btc from did:key identifier",
1318
+ { did, cause: err }
1319
+ );
1320
+ }
1321
+ if (decoded.length < 3) {
1322
+ throw new OmaTrustError("INVALID_DID", "did:key decoded bytes too short", { did });
1323
+ }
1324
+ const { value: codecValue, bytesRead } = readVarint(decoded, 0);
1325
+ const keyType = MULTICODEC_KEY_TYPES[codecValue];
1326
+ if (!keyType) {
1327
+ throw new OmaTrustError(
1328
+ "UNSUPPORTED_KEY_TYPE",
1329
+ `Unsupported multicodec key type 0x${codecValue.toString(16)} in did:key`,
1330
+ { did, codec: `0x${codecValue.toString(16)}` }
1331
+ );
1332
+ }
1333
+ const keyBytes = decoded.slice(bytesRead);
1334
+ if (keyType.kty === "OKP") {
1335
+ if (keyBytes.length !== 32) {
1336
+ throw new OmaTrustError(
1337
+ "INVALID_DID",
1338
+ `Expected 32-byte ${keyType.crv} key, got ${keyBytes.length} bytes`,
1339
+ { did, crv: keyType.crv }
1340
+ );
1341
+ }
1342
+ const jwk = {
1343
+ kty: keyType.kty,
1344
+ crv: keyType.crv,
1345
+ x: base64url.encode(keyBytes)
1346
+ };
1347
+ return jwkToDidJwk(jwk);
1348
+ }
1349
+ return decompressEcKey(keyBytes, keyType.crv).x.toString();
1350
+ }
353
1351
 
354
1352
  // src/reputation/index.ts
355
1353
  var reputation_exports = {};
356
1354
  __export(reputation_exports, {
1355
+ EIP1967_ADMIN_SLOT: () => EIP1967_ADMIN_SLOT,
1356
+ OWNERSHIP_PATTERNS: () => OWNERSHIP_PATTERNS,
357
1357
  buildDelegatedAttestationTypedData: () => buildDelegatedAttestationTypedData,
358
1358
  buildDelegatedTypedDataFromEncoded: () => buildDelegatedTypedDataFromEncoded,
359
1359
  buildDnsTxtRecord: () => buildDnsTxtRecord,
@@ -372,9 +1372,12 @@ __export(reputation_exports, {
372
1372
  createX402ReceiptProof: () => createX402ReceiptProof,
373
1373
  decodeAttestationData: () => decodeAttestationData,
374
1374
  deduplicateReviews: () => deduplicateReviews,
1375
+ discoverContractOwner: () => discoverContractOwner,
1376
+ discoverControllingWalletDid: () => discoverControllingWalletDid,
375
1377
  encodeAttestationData: () => encodeAttestationData,
376
- extractAddressesFromDidDocument: () => extractAddressesFromDidDocument,
1378
+ extractEvmAddressesFromDidDocument: () => extractEvmAddressesFromDidDocument,
377
1379
  extractExpirationTime: () => extractExpirationTime,
1380
+ extractJwksFromDidDocument: () => extractJwksFromDidDocument,
378
1381
  fetchDidDocument: () => fetchDidDocument,
379
1382
  formatSchemaUid: () => formatSchemaUid,
380
1383
  formatTransferAmount: () => formatTransferAmount,
@@ -382,6 +1385,7 @@ __export(reputation_exports, {
382
1385
  getAttestationsByAttester: () => getAttestationsByAttester,
383
1386
  getAttestationsForDid: () => getAttestationsForDid,
384
1387
  getChainConstants: () => getChainConstants,
1388
+ getControllerAuthorization: () => getControllerAuthorization,
385
1389
  getExplorerAddressUrl: () => getExplorerAddressUrl,
386
1390
  getExplorerTxUrl: () => getExplorerTxUrl,
387
1391
  getLatestAttestations: () => getLatestAttestations,
@@ -395,6 +1399,8 @@ __export(reputation_exports, {
395
1399
  normalizeSchema: () => normalizeSchema,
396
1400
  parseDnsTxtRecord: () => parseDnsTxtRecord,
397
1401
  prepareDelegatedAttestation: () => prepareDelegatedAttestation,
1402
+ readOwnerFromContract: () => readOwnerFromContract,
1403
+ requestControllerWitness: () => requestControllerWitness,
398
1404
  revokeAttestation: () => revokeAttestation,
399
1405
  schemaToString: () => schemaToString,
400
1406
  splitSignature: () => splitSignature,
@@ -406,11 +1412,21 @@ __export(reputation_exports, {
406
1412
  verifyDidJsonControllerDid: () => verifyDidJsonControllerDid,
407
1413
  verifyDidPkhOwnership: () => verifyDidPkhOwnership,
408
1414
  verifyDidWebOwnership: () => verifyDidWebOwnership,
409
- verifyDnsTxtControllerDid: () => verifyDnsTxtControllerDid,
1415
+ verifyDnsTxtControllerDid: () => verifyDnsTxtControllerDid2,
410
1416
  verifyEip712Signature: () => verifyEip712Signature,
1417
+ verifyKeyBindingProofs: () => verifyKeyBindingProofs,
1418
+ verifyLinkedIdentifierProofs: () => verifyLinkedIdentifierProofs,
411
1419
  verifyProof: () => verifyProof,
412
1420
  verifySchemaExists: () => verifySchemaExists,
413
- verifySubjectOwnership: () => verifySubjectOwnership
1421
+ verifySubjectOwnership: () => verifySubjectOwnership,
1422
+ verifyTransferProof: () => verifyTransferProof,
1423
+ verifyX402Artifact: () => verifyX402Artifact,
1424
+ verifyX402Eip712Artifact: () => verifyX402Eip712Artifact,
1425
+ verifyX402Eip712Offer: () => verifyX402Eip712Offer,
1426
+ verifyX402Eip712Receipt: () => verifyX402Eip712Receipt,
1427
+ verifyX402JwsArtifact: () => verifyX402JwsArtifact,
1428
+ verifyX402JwsOffer: () => verifyX402JwsOffer,
1429
+ verifyX402JwsReceipt: () => verifyX402JwsReceipt
414
1430
  });
415
1431
  function normalizeSchema(schema) {
416
1432
  if (Array.isArray(schema)) {
@@ -869,19 +1885,32 @@ async function submitDelegatedAttestation(params) {
869
1885
  payload = {};
870
1886
  }
871
1887
  if (!response.ok) {
872
- throw new OmaTrustError("NETWORK_ERROR", "Relay submission failed", {
873
- status: response.status,
1888
+ const relayError = {
1889
+ httpStatus: response.status,
1890
+ code: payload.code,
1891
+ error: payload.error ?? payload.message,
874
1892
  payload
875
- });
1893
+ };
1894
+ throw new OmaTrustError("RELAY_ERROR", `Relay submission failed (HTTP ${response.status})`, relayError);
876
1895
  }
877
1896
  const uid = payload.uid ?? ZERO_UID;
878
1897
  const txHash = payload.txHash;
879
- const status = payload.status ?? "submitted";
880
- return {
881
- uid,
882
- txHash,
883
- status
884
- };
1898
+ const hasConfirmationSignals = payload.status === "confirmed" || payload.success === true || typeof payload.blockNumber === "number";
1899
+ const status = hasConfirmationSignals ? "confirmed" : "submitted";
1900
+ const relay = {};
1901
+ if (typeof payload.blockNumber === "number") relay.blockNumber = payload.blockNumber;
1902
+ if (typeof payload.chain === "string") relay.chain = payload.chain;
1903
+ if (typeof payload.success === "boolean") relay.success = payload.success;
1904
+ for (const key of Object.keys(payload)) {
1905
+ if (!["uid", "txHash", "status", "blockNumber", "chain", "success"].includes(key)) {
1906
+ relay[key] = payload[key];
1907
+ }
1908
+ }
1909
+ const result = { uid, txHash, status };
1910
+ if (Object.keys(relay).length > 0) {
1911
+ result.relay = relay;
1912
+ }
1913
+ return result;
885
1914
  }
886
1915
  var EAS_EVENT_ABI = [
887
1916
  "event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
@@ -983,7 +2012,7 @@ async function getAttestationsForDid(params) {
983
2012
  provider,
984
2013
  fromBlock,
985
2014
  toBlock,
986
- { recipient: didToAddress(params.did), schemas: params.schemas }
2015
+ { recipient: didToAddress(params.subjectDid), schemas: params.schemas }
987
2016
  );
988
2017
  }
989
2018
  async function getAttestationsByAttester(params) {
@@ -1224,25 +2253,7 @@ function getExplorerTxUrl(chainId, txHash) {
1224
2253
  function getExplorerAddressUrl(chainId, address) {
1225
2254
  return `${getConfig(chainId).explorer}/address/${address}`;
1226
2255
  }
1227
- async function fetchDidDocument(domain) {
1228
- const normalized = domain.toLowerCase().replace(/\.$/, "");
1229
- const url = `https://${normalized}/.well-known/did.json`;
1230
- let response;
1231
- try {
1232
- response = await fetch(url, { headers: { Accept: "application/json" } });
1233
- } catch (err) {
1234
- throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch DID document", { domain, err });
1235
- }
1236
- if (!response.ok) {
1237
- throw new OmaTrustError("NETWORK_ERROR", "DID document fetch failed", {
1238
- domain,
1239
- status: response.status
1240
- });
1241
- }
1242
- const body = await response.json();
1243
- return body;
1244
- }
1245
- function extractAddressesFromDidDocument(didDocument) {
2256
+ function extractEvmAddressesFromDidDocument(didDocument) {
1246
2257
  const methods = didDocument.verificationMethod;
1247
2258
  if (!Array.isArray(methods)) {
1248
2259
  return [];
@@ -1266,89 +2277,453 @@ function extractAddressesFromDidDocument(didDocument) {
1266
2277
  }
1267
2278
  return [...addresses];
1268
2279
  }
2280
+ function extractJwksFromDidDocument(didDocument) {
2281
+ const methods = didDocument.verificationMethod;
2282
+ if (!Array.isArray(methods)) {
2283
+ return [];
2284
+ }
2285
+ const jwks = [];
2286
+ for (const method of methods) {
2287
+ const publicKeyJwk = method.publicKeyJwk;
2288
+ if (publicKeyJwk && typeof publicKeyJwk === "object" && !Array.isArray(publicKeyJwk)) {
2289
+ jwks.push(publicKeyJwk);
2290
+ }
2291
+ }
2292
+ return jwks;
2293
+ }
1269
2294
  function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
2295
+ const method = extractDidMethod(expectedControllerDid);
2296
+ if (method === "jwk") {
2297
+ try {
2298
+ const expectedJwk = didJwkToJwk(expectedControllerDid);
2299
+ const documentJwks = extractJwksFromDidDocument(didDocument);
2300
+ for (const docJwk of documentJwks) {
2301
+ try {
2302
+ if (publicJwkEquals(docJwk, expectedJwk)) {
2303
+ return { valid: true };
2304
+ }
2305
+ } catch {
2306
+ }
2307
+ }
2308
+ return {
2309
+ valid: false,
2310
+ reason: "No matching publicKeyJwk found in DID document verification methods"
2311
+ };
2312
+ } catch {
2313
+ return {
2314
+ valid: false,
2315
+ reason: "Failed to decode did:jwk controller DID"
2316
+ };
2317
+ }
2318
+ }
1270
2319
  let expectedAddress;
1271
2320
  try {
1272
2321
  expectedAddress = getAddress(extractAddressFromDid(expectedControllerDid));
1273
2322
  } catch {
1274
- return { valid: false, reason: "Expected controller DID does not resolve to an EVM address" };
2323
+ return { valid: false, reason: "Controller DID does not resolve to an EVM address and is not did:jwk" };
2324
+ }
2325
+ const addresses = extractEvmAddressesFromDidDocument(didDocument);
2326
+ if (addresses.some((address) => address.toLowerCase() === expectedAddress.toLowerCase())) {
2327
+ return { valid: true };
2328
+ }
2329
+ return {
2330
+ valid: false,
2331
+ reason: `No matching address found in DID document (expected ${expectedAddress})`
2332
+ };
2333
+ }
2334
+ async function verifyDidJsonControllerDid(domain, expectedControllerDid, options = {}) {
2335
+ if (!domain || typeof domain !== "string") {
2336
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
2337
+ }
2338
+ const didDocument = options.fetchDidDocument ? await options.fetchDidDocument(domain) : await fetchDidDocument(domain);
2339
+ return verifyDidDocumentControllerDid(didDocument, expectedControllerDid);
2340
+ }
2341
+ function buildEip712Domain(name, version, chainId, verifyingContract) {
2342
+ return { name, version, chainId, verifyingContract };
2343
+ }
2344
+ function getOmaTrustProofEip712Types() {
2345
+ return {
2346
+ primaryType: "OmaTrustProof",
2347
+ types: {
2348
+ OmaTrustProof: [
2349
+ { name: "signer", type: "address" },
2350
+ { name: "authorizedEntity", type: "string" },
2351
+ { name: "signingPurpose", type: "string" },
2352
+ { name: "creationTimestamp", type: "uint256" },
2353
+ { name: "expirationTimestamp", type: "uint256" },
2354
+ { name: "randomValue", type: "bytes32" },
2355
+ { name: "statement", type: "string" }
2356
+ ]
2357
+ }
2358
+ };
2359
+ }
2360
+ function verifyEip712Signature(typedData, signature) {
2361
+ try {
2362
+ const signer = verifyTypedData(
2363
+ typedData.domain,
2364
+ typedData.types,
2365
+ typedData.message,
2366
+ signature
2367
+ );
2368
+ return { valid: true, signer };
2369
+ } catch (err) {
2370
+ throw new OmaTrustError("INVALID_INPUT", "Failed to verify EIP-712 signature", { err });
2371
+ }
2372
+ }
2373
+
2374
+ // src/reputation/proof/dns-txt-record.ts
2375
+ function parseDnsTxtRecord(record) {
2376
+ if (!record || typeof record !== "string") {
2377
+ throw new OmaTrustError("INVALID_INPUT", "record must be a non-empty string", { record });
2378
+ }
2379
+ const entries = record.split(/[;\s]+/).map((entry) => entry.trim()).filter(Boolean);
2380
+ const parsed = {};
2381
+ const controllers = [];
2382
+ for (const entry of entries) {
2383
+ const eqIndex = entry.indexOf("=");
2384
+ if (eqIndex === -1) continue;
2385
+ const key = entry.slice(0, eqIndex).trim();
2386
+ const value = entry.slice(eqIndex + 1).trim();
2387
+ if (!key || !value) continue;
2388
+ if (key === "controller") {
2389
+ controllers.push(value);
2390
+ } else {
2391
+ parsed[key] = value;
2392
+ }
2393
+ }
2394
+ return {
2395
+ version: parsed.v,
2396
+ controller: controllers[0],
2397
+ controllers
2398
+ };
2399
+ }
2400
+ function buildDnsTxtRecord(controllerDid) {
2401
+ const normalized = normalizeDid(controllerDid);
2402
+ return `v=1;controller=${normalized}`;
2403
+ }
2404
+ var REQUIRED_OFFER_FIELDS = [
2405
+ "version",
2406
+ "resourceUrl",
2407
+ "scheme",
2408
+ "network",
2409
+ "asset",
2410
+ "payTo",
2411
+ "amount"
2412
+ ];
2413
+ var REQUIRED_RECEIPT_FIELDS = [
2414
+ "version",
2415
+ "network",
2416
+ "resourceUrl",
2417
+ "payer",
2418
+ "issuedAt"
2419
+ ];
2420
+ function validateOfferPayload(payload) {
2421
+ for (const field of REQUIRED_OFFER_FIELDS) {
2422
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
2423
+ return { valid: false, field };
2424
+ }
2425
+ }
2426
+ return { valid: true };
2427
+ }
2428
+ function validateReceiptPayload(payload) {
2429
+ for (const field of REQUIRED_RECEIPT_FIELDS) {
2430
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
2431
+ return { valid: false, field };
2432
+ }
2433
+ }
2434
+ return { valid: true };
2435
+ }
2436
+ function failure(code2, message, partial) {
2437
+ return {
2438
+ valid: false,
2439
+ error: { code: code2, message },
2440
+ ...partial
2441
+ };
2442
+ }
2443
+ async function verifyX402JwsArtifact(artifact, options) {
2444
+ if (!artifact || typeof artifact !== "object") {
2445
+ return failure("INVALID_ARTIFACT", "Artifact must be a non-null object");
2446
+ }
2447
+ if (artifact.format !== "jws") {
2448
+ return failure("INVALID_ARTIFACT", `Expected format "jws", got "${artifact.format}"`);
2449
+ }
2450
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
2451
+ return failure("INVALID_ARTIFACT", "Artifact signature must be a non-empty string");
2452
+ }
2453
+ const compactJws = artifact.signature;
2454
+ const parts = compactJws.split(".");
2455
+ if (parts.length !== 3) {
2456
+ return failure("MALFORMED_JWS", "Invalid compact JWS format: expected 3 dot-separated parts");
2457
+ }
2458
+ let header;
2459
+ try {
2460
+ header = decodeProtectedHeader(compactJws);
2461
+ } catch {
2462
+ return failure("MALFORMED_JWS", "Failed to decode JWS protected header");
2463
+ }
2464
+ if (!header.alg || typeof header.alg !== "string") {
2465
+ return failure("MISSING_ALG", "JWS header must include alg", { header });
2466
+ }
2467
+ const kid = typeof header.kid === "string" ? header.kid : null;
2468
+ const embeddedJwk = header.jwk;
2469
+ if (!kid && !embeddedJwk) {
2470
+ return failure(
2471
+ "MISSING_KEY_MATERIAL",
2472
+ "JWS header must include at least one of kid or jwk",
2473
+ { header }
2474
+ );
2475
+ }
2476
+ let publicKeyJwk;
2477
+ let publicKeySource;
2478
+ if (embeddedJwk) {
2479
+ const validation = validatePublicJwk(embeddedJwk);
2480
+ if (!validation.valid) {
2481
+ return failure(
2482
+ "INVALID_EMBEDDED_JWK",
2483
+ `Embedded JWK is invalid: ${validation.error}`,
2484
+ { header }
2485
+ );
2486
+ }
2487
+ publicKeyJwk = embeddedJwk;
2488
+ publicKeySource = "embedded-jwk";
2489
+ if (kid) {
2490
+ try {
2491
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
2492
+ if (!publicJwkEquals(publicKeyJwk, resolved.publicKeyJwk)) {
2493
+ return failure(
2494
+ "KEY_CONFLICT",
2495
+ "Embedded jwk conflicts with public key resolved from kid",
2496
+ { header, kid }
2497
+ );
2498
+ }
2499
+ } catch (err) {
2500
+ }
2501
+ }
2502
+ } else {
2503
+ publicKeySource = "kid-resolution";
2504
+ try {
2505
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
2506
+ publicKeyJwk = resolved.publicKeyJwk;
2507
+ } catch (err) {
2508
+ const message = err instanceof OmaTrustError ? `Failed to resolve kid "${kid}": ${err.message}` : `Failed to resolve kid "${kid}"`;
2509
+ return failure("KID_RESOLUTION_FAILED", message, { header, kid });
2510
+ }
2511
+ }
2512
+ let payload;
2513
+ try {
2514
+ const key = await importJWK(publicKeyJwk, header.alg);
2515
+ const result = await compactVerify(compactJws, key);
2516
+ const payloadText = new TextDecoder().decode(result.payload);
2517
+ payload = JSON.parse(payloadText);
2518
+ } catch (err) {
2519
+ const message = err instanceof Error ? err.message : "Signature verification failed";
2520
+ return failure("SIGNATURE_INVALID", message, { header, kid });
2521
+ }
2522
+ const publicKeyDid = jwkToDidJwk(publicKeyJwk);
2523
+ return {
2524
+ valid: true,
2525
+ header,
2526
+ payload,
2527
+ kid,
2528
+ publicKeyJwk,
2529
+ publicKeySource,
2530
+ publicKeyDid
2531
+ };
2532
+ }
2533
+ async function verifyX402JwsOffer(artifact, options) {
2534
+ const result = await verifyX402JwsArtifact(artifact, options);
2535
+ if (!result.valid) return result;
2536
+ const payloadCheck = validateOfferPayload(result.payload);
2537
+ if (!payloadCheck.valid) {
2538
+ return failure(
2539
+ "INVALID_OFFER_PAYLOAD",
2540
+ `Missing required offer field: ${payloadCheck.field}`,
2541
+ {
2542
+ header: result.header,
2543
+ payload: result.payload,
2544
+ kid: result.kid,
2545
+ publicKeyJwk: result.publicKeyJwk,
2546
+ publicKeySource: result.publicKeySource,
2547
+ publicKeyDid: result.publicKeyDid
2548
+ }
2549
+ );
2550
+ }
2551
+ return result;
2552
+ }
2553
+ async function verifyX402JwsReceipt(artifact, options) {
2554
+ const result = await verifyX402JwsArtifact(artifact, options);
2555
+ if (!result.valid) return result;
2556
+ const payloadCheck = validateReceiptPayload(result.payload);
2557
+ if (!payloadCheck.valid) {
2558
+ return failure(
2559
+ "INVALID_RECEIPT_PAYLOAD",
2560
+ `Missing required receipt field: ${payloadCheck.field}`,
2561
+ {
2562
+ header: result.header,
2563
+ payload: result.payload,
2564
+ kid: result.kid,
2565
+ publicKeyJwk: result.publicKeyJwk,
2566
+ publicKeySource: result.publicKeySource,
2567
+ publicKeyDid: result.publicKeyDid
2568
+ }
2569
+ );
2570
+ }
2571
+ return result;
2572
+ }
2573
+ var OFFER_DOMAIN = {
2574
+ name: "x402 offer",
2575
+ version: "1",
2576
+ chainId: 1
2577
+ };
2578
+ var RECEIPT_DOMAIN = {
2579
+ name: "x402 receipt",
2580
+ version: "1",
2581
+ chainId: 1
2582
+ };
2583
+ var OFFER_TYPES = {
2584
+ Offer: [
2585
+ { name: "version", type: "uint256" },
2586
+ { name: "resourceUrl", type: "string" },
2587
+ { name: "scheme", type: "string" },
2588
+ { name: "network", type: "string" },
2589
+ { name: "asset", type: "string" },
2590
+ { name: "payTo", type: "string" },
2591
+ { name: "amount", type: "string" },
2592
+ { name: "validUntil", type: "uint256" }
2593
+ ]
2594
+ };
2595
+ var RECEIPT_TYPES = {
2596
+ Receipt: [
2597
+ { name: "version", type: "uint256" },
2598
+ { name: "network", type: "string" },
2599
+ { name: "resourceUrl", type: "string" },
2600
+ { name: "payer", type: "string" },
2601
+ { name: "issuedAt", type: "uint256" },
2602
+ { name: "transaction", type: "string" }
2603
+ ]
2604
+ };
2605
+ var REQUIRED_OFFER_FIELDS2 = [
2606
+ "version",
2607
+ "resourceUrl",
2608
+ "scheme",
2609
+ "network",
2610
+ "asset",
2611
+ "payTo",
2612
+ "amount"
2613
+ ];
2614
+ var REQUIRED_RECEIPT_FIELDS2 = [
2615
+ "version",
2616
+ "network",
2617
+ "resourceUrl",
2618
+ "payer",
2619
+ "issuedAt"
2620
+ ];
2621
+ function validateOfferPayload2(payload) {
2622
+ for (const field of REQUIRED_OFFER_FIELDS2) {
2623
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
2624
+ return { valid: false, field };
2625
+ }
1275
2626
  }
1276
- const addresses = extractAddressesFromDidDocument(didDocument);
1277
- if (addresses.some((address) => address.toLowerCase() === expectedAddress.toLowerCase())) {
1278
- return { valid: true };
2627
+ return { valid: true };
2628
+ }
2629
+ function validateReceiptPayload2(payload) {
2630
+ for (const field of REQUIRED_RECEIPT_FIELDS2) {
2631
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
2632
+ return { valid: false, field };
2633
+ }
1279
2634
  }
2635
+ return { valid: true };
2636
+ }
2637
+ function failure2(code2, message, partial) {
1280
2638
  return {
1281
2639
  valid: false,
1282
- reason: `No matching address found in DID document (expected ${expectedAddress})`
2640
+ error: { code: code2, message },
2641
+ ...partial
1283
2642
  };
1284
2643
  }
1285
- async function verifyDidJsonControllerDid(domain, expectedControllerDid, options = {}) {
1286
- if (!domain || typeof domain !== "string") {
1287
- throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1288
- }
1289
- const didDocument = options.fetchDidDocument ? await options.fetchDidDocument(domain) : await fetchDidDocument(domain);
1290
- return verifyDidDocumentControllerDid(didDocument, expectedControllerDid);
1291
- }
1292
- function buildEip712Domain(name, version, chainId, verifyingContract) {
1293
- return { name, version, chainId, verifyingContract };
2644
+ function prepareOfferMessage(payload) {
2645
+ return {
2646
+ version: payload.version,
2647
+ resourceUrl: payload.resourceUrl,
2648
+ scheme: payload.scheme,
2649
+ network: payload.network,
2650
+ asset: payload.asset,
2651
+ payTo: payload.payTo,
2652
+ amount: payload.amount,
2653
+ validUntil: payload.validUntil ?? 0
2654
+ };
1294
2655
  }
1295
- function getOmaTrustProofEip712Types() {
2656
+ function prepareReceiptMessage(payload) {
1296
2657
  return {
1297
- primaryType: "OmaTrustProof",
1298
- types: {
1299
- OmaTrustProof: [
1300
- { name: "signer", type: "address" },
1301
- { name: "authorizedEntity", type: "string" },
1302
- { name: "signingPurpose", type: "string" },
1303
- { name: "creationTimestamp", type: "uint256" },
1304
- { name: "expirationTimestamp", type: "uint256" },
1305
- { name: "randomValue", type: "bytes32" },
1306
- { name: "statement", type: "string" }
1307
- ]
1308
- }
2658
+ version: payload.version,
2659
+ network: payload.network,
2660
+ resourceUrl: payload.resourceUrl,
2661
+ payer: payload.payer,
2662
+ issuedAt: payload.issuedAt,
2663
+ transaction: payload.transaction ?? ""
1309
2664
  };
1310
2665
  }
1311
- function verifyEip712Signature(typedData, signature) {
1312
- try {
1313
- const signer = verifyTypedData(
1314
- typedData.domain,
1315
- typedData.types,
1316
- typedData.message,
1317
- signature
1318
- );
1319
- return { valid: true, signer };
1320
- } catch (err) {
1321
- throw new OmaTrustError("INVALID_INPUT", "Failed to verify EIP-712 signature", { err });
2666
+ function verifyX402Eip712Artifact(artifact, artifactType) {
2667
+ if (!artifact || typeof artifact !== "object") {
2668
+ return failure2("INVALID_ARTIFACT", "Artifact must be a non-null object");
1322
2669
  }
1323
- }
1324
-
1325
- // src/reputation/proof/dns-txt-record.ts
1326
- function parseDnsTxtRecord(record) {
1327
- if (!record || typeof record !== "string") {
1328
- throw new OmaTrustError("INVALID_INPUT", "record must be a non-empty string", { record });
2670
+ if (artifact.format !== "eip712") {
2671
+ return failure2("INVALID_ARTIFACT", `Expected format "eip712", got "${artifact.format}"`);
1329
2672
  }
1330
- const entries = record.split(/[;\s]+/).map((entry) => entry.trim()).filter(Boolean);
1331
- const parsed = {};
1332
- for (const entry of entries) {
1333
- const [key, ...valueParts] = entry.split("=");
1334
- if (!key) {
1335
- continue;
2673
+ if (!artifact.payload || typeof artifact.payload !== "object" || Array.isArray(artifact.payload)) {
2674
+ return failure2("MISSING_PAYLOAD", "EIP-712 artifact must include a payload object");
2675
+ }
2676
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
2677
+ return failure2("MISSING_SIGNATURE", "EIP-712 artifact must include a non-empty signature");
2678
+ }
2679
+ const payload = artifact.payload;
2680
+ if (artifactType === "offer") {
2681
+ const check = validateOfferPayload2(payload);
2682
+ if (!check.valid) {
2683
+ return failure2(
2684
+ "INVALID_OFFER_PAYLOAD",
2685
+ `Missing required offer field: ${check.field}`,
2686
+ { payload }
2687
+ );
1336
2688
  }
1337
- const value = valueParts.join("=");
1338
- if (!value) {
1339
- continue;
2689
+ } else {
2690
+ const check = validateReceiptPayload2(payload);
2691
+ if (!check.valid) {
2692
+ return failure2(
2693
+ "INVALID_RECEIPT_PAYLOAD",
2694
+ `Missing required receipt field: ${check.field}`,
2695
+ { payload }
2696
+ );
1340
2697
  }
1341
- parsed[key.trim()] = value.trim();
2698
+ }
2699
+ const domain = artifactType === "offer" ? OFFER_DOMAIN : RECEIPT_DOMAIN;
2700
+ const types = artifactType === "offer" ? OFFER_TYPES : RECEIPT_TYPES;
2701
+ const message = artifactType === "offer" ? prepareOfferMessage(payload) : prepareReceiptMessage(payload);
2702
+ let signer;
2703
+ try {
2704
+ signer = verifyTypedData(
2705
+ domain,
2706
+ types,
2707
+ message,
2708
+ artifact.signature
2709
+ );
2710
+ signer = getAddress(signer);
2711
+ } catch (err) {
2712
+ const message2 = err instanceof Error ? err.message : "EIP-712 signature verification failed";
2713
+ return failure2("SIGNATURE_INVALID", message2, { payload });
1342
2714
  }
1343
2715
  return {
1344
- version: parsed.v,
1345
- controller: parsed.controller,
1346
- ...parsed
2716
+ valid: true,
2717
+ payload,
2718
+ signer,
2719
+ artifactType
1347
2720
  };
1348
2721
  }
1349
- function buildDnsTxtRecord(controllerDid) {
1350
- const normalized = normalizeDid(controllerDid);
1351
- return `v=1;controller=${normalized}`;
2722
+ function verifyX402Eip712Offer(artifact) {
2723
+ return verifyX402Eip712Artifact(artifact, "offer");
2724
+ }
2725
+ function verifyX402Eip712Receipt(artifact) {
2726
+ return verifyX402Eip712Artifact(artifact, "receipt");
1352
2727
  }
1353
2728
 
1354
2729
  // src/reputation/verify.ts
@@ -1408,18 +2783,18 @@ function decodeJwtPayload(compactJws) {
1408
2783
  return JSON.parse(json);
1409
2784
  }
1410
2785
  async function verifyProof(params) {
1411
- const { proof, provider, expectedSubject, expectedController } = params;
2786
+ const { proof, provider, expectedSubjectDid, expectedControllerDid } = params;
1412
2787
  try {
1413
2788
  switch (proof.proofType) {
1414
2789
  case "tx-encoded-value": {
1415
2790
  if (!provider) {
1416
2791
  return { valid: false, proofType: proof.proofType, reason: "Provider is required" };
1417
2792
  }
1418
- if (!expectedSubject || !expectedController) {
2793
+ if (!expectedSubjectDid || !expectedControllerDid) {
1419
2794
  return {
1420
2795
  valid: false,
1421
2796
  proofType: proof.proofType,
1422
- reason: "expectedSubject and expectedController are required"
2797
+ reason: "expectedSubjectDid and expectedControllerDid are required"
1423
2798
  };
1424
2799
  }
1425
2800
  const proofObject = proof.proofObject;
@@ -1431,13 +2806,13 @@ async function verifyProof(params) {
1431
2806
  return { valid: false, proofType: proof.proofType, reason: "Transaction not found" };
1432
2807
  }
1433
2808
  const expectedAmount = calculateTransferAmount(
1434
- expectedSubject,
1435
- expectedController,
2809
+ expectedSubjectDid,
2810
+ expectedControllerDid,
1436
2811
  chainId,
1437
2812
  getProofPurpose(proof)
1438
2813
  );
1439
- const subjectAddress = getAddress(extractAddressFromDid(expectedSubject));
1440
- const controllerAddress = getAddress(extractAddressFromDid(expectedController));
2814
+ const subjectAddress = getAddress(extractAddressFromDid(expectedSubjectDid));
2815
+ const controllerAddress = getAddress(extractAddressFromDid(expectedControllerDid));
1441
2816
  if (tx.from && getAddress(tx.from) !== subjectAddress) {
1442
2817
  return { valid: false, proofType: proof.proofType, reason: "Transaction sender mismatch" };
1443
2818
  }
@@ -1511,6 +2886,38 @@ async function verifyProof(params) {
1511
2886
  if (!proof.proofObject || typeof proof.proofObject !== "object") {
1512
2887
  return { valid: false, proofType: proof.proofType, reason: "Invalid x402 proof object" };
1513
2888
  }
2889
+ const proofObj = proof.proofObject;
2890
+ if (proofObj.format === "jws" && typeof proofObj.signature === "string") {
2891
+ const artifact = {
2892
+ format: "jws",
2893
+ signature: proofObj.signature
2894
+ };
2895
+ const jwsResult = proof.proofType === "x402-offer" ? await verifyX402JwsOffer(artifact) : await verifyX402JwsReceipt(artifact);
2896
+ if (!jwsResult.valid) {
2897
+ return {
2898
+ valid: false,
2899
+ proofType: proof.proofType,
2900
+ reason: jwsResult.error?.message ?? "JWS verification failed"
2901
+ };
2902
+ }
2903
+ return { valid: true, proofType: proof.proofType };
2904
+ }
2905
+ if (proofObj.format === "eip712" && typeof proofObj.signature === "string" && proofObj.payload && typeof proofObj.payload === "object") {
2906
+ const artifact = {
2907
+ format: "eip712",
2908
+ payload: proofObj.payload,
2909
+ signature: proofObj.signature
2910
+ };
2911
+ const eip712Result = proof.proofType === "x402-offer" ? verifyX402Eip712Offer(artifact) : verifyX402Eip712Receipt(artifact);
2912
+ if (!eip712Result.valid) {
2913
+ return {
2914
+ valid: false,
2915
+ proofType: proof.proofType,
2916
+ reason: eip712Result.error?.message ?? "EIP-712 verification failed"
2917
+ };
2918
+ }
2919
+ return { valid: true, proofType: proof.proofType };
2920
+ }
1514
2921
  return { valid: true, proofType: proof.proofType };
1515
2922
  }
1516
2923
  case "evidence-pointer": {
@@ -1522,9 +2929,9 @@ async function verifyProof(params) {
1522
2929
  if (!response.ok) {
1523
2930
  return { valid: false, proofType: proof.proofType, reason: `Evidence fetch failed (${response.status})` };
1524
2931
  }
1525
- if (object.url.endsWith("/.well-known/did.json") && expectedController) {
2932
+ if (object.url.endsWith("/.well-known/did.json") && expectedControllerDid) {
1526
2933
  const didDoc = await response.json();
1527
- const didCheck = verifyDidDocumentControllerDid(didDoc, expectedController);
2934
+ const didCheck = verifyDidDocumentControllerDid(didDoc, expectedControllerDid);
1528
2935
  return {
1529
2936
  valid: didCheck.valid,
1530
2937
  proofType: proof.proofType,
@@ -1532,10 +2939,10 @@ async function verifyProof(params) {
1532
2939
  };
1533
2940
  }
1534
2941
  const body = await response.text();
1535
- if (expectedController && !body.includes(expectedController)) {
2942
+ if (expectedControllerDid && !body.includes(expectedControllerDid)) {
1536
2943
  try {
1537
2944
  const parsed = parseDnsTxtRecord(body);
1538
- if (parsed.controller !== expectedController) {
2945
+ if (!parsed.controllers.includes(expectedControllerDid)) {
1539
2946
  return {
1540
2947
  valid: false,
1541
2948
  proofType: proof.proofType,
@@ -1591,8 +2998,8 @@ async function verifyAttestation(params) {
1591
2998
  const result = await verifyProof({
1592
2999
  proof,
1593
3000
  provider: params.provider,
1594
- expectedSubject: params.context?.subject,
1595
- expectedController: params.context?.controller
3001
+ expectedSubjectDid: params.context?.subjectDid,
3002
+ expectedControllerDid: params.context?.controllerDid
1596
3003
  });
1597
3004
  checks[proof.proofType] = result.valid;
1598
3005
  if (!result.valid) {
@@ -1675,25 +3082,479 @@ async function callMethod(params, method) {
1675
3082
  if (!response.ok) {
1676
3083
  return null;
1677
3084
  }
1678
- return {
1679
- ok: true,
1680
- method,
1681
- details
1682
- };
3085
+ return {
3086
+ ok: true,
3087
+ method,
3088
+ details
3089
+ };
3090
+ }
3091
+ async function callControllerWitness(params) {
3092
+ const dnsResult = await callMethod(params, "dns-txt").catch(() => null);
3093
+ if (dnsResult) {
3094
+ return dnsResult;
3095
+ }
3096
+ const didJsonResult = await callMethod(params, "did-json").catch(() => null);
3097
+ if (didJsonResult) {
3098
+ return didJsonResult;
3099
+ }
3100
+ return {
3101
+ ok: false,
3102
+ method: "did-json"
3103
+ };
3104
+ }
3105
+ var DEFAULT_CONTROLLER_WITNESS_URL = "https://api.omatrust.org/v1/controller-witness";
3106
+ async function requestControllerWitness(params) {
3107
+ const url = params.gatewayUrl ?? DEFAULT_CONTROLLER_WITNESS_URL;
3108
+ const body = {
3109
+ subjectDid: params.subjectDid,
3110
+ controllerDid: params.controllerDid
3111
+ };
3112
+ if (params.chainId !== void 0) {
3113
+ body.chainId = params.chainId;
3114
+ }
3115
+ let response;
3116
+ try {
3117
+ response = await fetch(url, {
3118
+ method: "POST",
3119
+ headers: { "Content-Type": "application/json" },
3120
+ credentials: "include",
3121
+ body: JSON.stringify(body),
3122
+ signal: AbortSignal.timeout(params.timeoutMs ?? 15e3)
3123
+ });
3124
+ } catch (err) {
3125
+ throw new OmaTrustError("NETWORK_ERROR", "Controller witness request failed", { err });
3126
+ }
3127
+ if (!response.ok) {
3128
+ const errorBody = await response.json().catch(() => ({ error: response.statusText }));
3129
+ throw new OmaTrustError(
3130
+ "API_ERROR",
3131
+ errorBody.error ?? `Controller witness failed: ${response.status}`,
3132
+ { status: response.status, ...errorBody }
3133
+ );
3134
+ }
3135
+ return await response.json();
3136
+ }
3137
+ var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
3138
+ var OWNERSHIP_PATTERNS = [
3139
+ { method: "owner", signature: "function owner() view returns (address)" },
3140
+ { method: "admin", signature: "function admin() view returns (address)" },
3141
+ { method: "getOwner", signature: "function getOwner() view returns (address)" }
3142
+ ];
3143
+ async function readOwnerFromContract(provider, contractAddress, signature, method) {
3144
+ try {
3145
+ const iface = new Interface([signature]);
3146
+ const data = iface.encodeFunctionData(method, []);
3147
+ const result = await provider.call({ to: contractAddress, data });
3148
+ const [value] = iface.decodeFunctionResult(method, result);
3149
+ if (typeof value === "string" && isAddress(value) && value !== ZeroAddress) {
3150
+ return getAddress(value);
3151
+ }
3152
+ } catch {
3153
+ }
3154
+ return null;
3155
+ }
3156
+ async function discoverContractOwner(provider, contractAddress) {
3157
+ try {
3158
+ const code2 = await provider.getCode(contractAddress);
3159
+ if (code2 === "0x" || code2 === "0x0") {
3160
+ return null;
3161
+ }
3162
+ } catch {
3163
+ return null;
3164
+ }
3165
+ for (const pattern of OWNERSHIP_PATTERNS) {
3166
+ const address = await readOwnerFromContract(
3167
+ provider,
3168
+ contractAddress,
3169
+ pattern.signature,
3170
+ pattern.method
3171
+ );
3172
+ if (address) {
3173
+ return address;
3174
+ }
3175
+ }
3176
+ try {
3177
+ const adminValue = await provider.getStorage(contractAddress, EIP1967_ADMIN_SLOT);
3178
+ if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
3179
+ const adminAddress = getAddress(`0x${adminValue.slice(-40)}`);
3180
+ if (adminAddress !== ZeroAddress) {
3181
+ return adminAddress;
3182
+ }
3183
+ }
3184
+ } catch {
3185
+ }
3186
+ return null;
3187
+ }
3188
+ async function discoverControllingWalletDid(provider, contractAddress, chainId) {
3189
+ const owner = await discoverContractOwner(provider, contractAddress);
3190
+ return owner ? buildEvmDidPkh(chainId, owner) : null;
3191
+ }
3192
+ async function verifyTransferProof(provider, txHash, subjectDid, attesterAddress, chainId) {
3193
+ try {
3194
+ const tx = await provider.getTransaction(txHash);
3195
+ if (!tx || !tx.from || !tx.to) {
3196
+ return false;
3197
+ }
3198
+ if (getAddress(tx.to) !== getAddress(attesterAddress)) {
3199
+ return false;
3200
+ }
3201
+ const attesterDid = buildEvmDidPkh(chainId, attesterAddress);
3202
+ const expectedAmount = calculateTransferAmount(
3203
+ subjectDid,
3204
+ attesterDid,
3205
+ chainId,
3206
+ "shared-control"
3207
+ );
3208
+ const actualValue = BigInt(tx.value ?? 0n);
3209
+ return actualValue === expectedAmount;
3210
+ } catch {
3211
+ return false;
3212
+ }
3213
+ }
3214
+
3215
+ // src/shared/trust-anchors.ts
3216
+ var DEFAULT_TRUST_ANCHORS_URL = "https://api.omatrust.org/v1/trust-anchors";
3217
+ var TRUST_ANCHORS_URL = DEFAULT_TRUST_ANCHORS_URL;
3218
+ var cachedAnchors = null;
3219
+ var cacheTimestamp = 0;
3220
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
3221
+ async function fetchTrustAnchors(url) {
3222
+ const now = Date.now();
3223
+ if (cachedAnchors && now - cacheTimestamp < CACHE_TTL_MS) {
3224
+ return cachedAnchors;
3225
+ }
3226
+ let res;
3227
+ try {
3228
+ res = await fetch(url ?? TRUST_ANCHORS_URL);
3229
+ } catch (err) {
3230
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch trust anchors", { err });
3231
+ }
3232
+ if (!res.ok) {
3233
+ throw new OmaTrustError("NETWORK_ERROR", `Failed to fetch trust anchors: ${res.status} ${res.statusText}`);
3234
+ }
3235
+ const anchors = await res.json();
3236
+ if (!anchors.version || !anchors.chains || typeof anchors.chains !== "object") {
3237
+ throw new OmaTrustError("INVALID_INPUT", "Invalid trust anchors format");
3238
+ }
3239
+ cachedAnchors = anchors;
3240
+ cacheTimestamp = now;
3241
+ return anchors;
3242
+ }
3243
+ function getChainAnchors(anchors, caip2) {
3244
+ const chain = anchors.chains[caip2];
3245
+ if (!chain) {
3246
+ throw new OmaTrustError("UNSUPPORTED_CHAIN", `Chain ${caip2} is not in the trust anchors`, {
3247
+ caip2,
3248
+ supportedChains: Object.keys(anchors.chains)
3249
+ });
3250
+ }
3251
+ return chain;
3252
+ }
3253
+
3254
+ // src/reputation/proof/dns-txt-shared.ts
3255
+ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options = {}) {
3256
+ if (!domain || typeof domain !== "string") {
3257
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
3258
+ }
3259
+ if (!options.resolveTxt) {
3260
+ throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
3261
+ }
3262
+ const prefix = options.recordPrefix ?? "_controllers";
3263
+ const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
3264
+ let records;
3265
+ try {
3266
+ records = await options.resolveTxt(host);
3267
+ } catch (err) {
3268
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
3269
+ }
3270
+ for (const recordParts of records) {
3271
+ const record = recordParts.join("");
3272
+ const parsed = parseDnsTxtRecord(record);
3273
+ if (parsed.version !== "1") continue;
3274
+ for (const recordController of parsed.controllers) {
3275
+ if (isSameControllerId(recordController, expectedControllerDid)) {
3276
+ return { valid: true, record };
3277
+ }
3278
+ }
3279
+ }
3280
+ return { valid: false, reason: "Controller DID not found in DNS TXT records" };
3281
+ }
3282
+
3283
+ // src/reputation/attester-authorization.ts
3284
+ var DEFAULT_CHAIN = "eip155:6623";
3285
+ var DEFAULT_PURPOSES = ["authentication", "assertionMethod"];
3286
+ async function getControllerAuthorization(params) {
3287
+ const chain = params.chain ?? DEFAULT_CHAIN;
3288
+ const purposes = params.purpose && params.purpose.length > 0 ? params.purpose : DEFAULT_PURPOSES;
3289
+ const controllerDid = normalizeDid(params.controllerDid);
3290
+ const controllerMethod = extractDidMethod(controllerDid);
3291
+ const controllerEvmAddress = extractControllerEvmAddress(controllerDid);
3292
+ const anchors = await fetchTrustAnchors();
3293
+ const chainAnchors = getChainAnchors(anchors, chain);
3294
+ const easContractAddress = params.easContractAddress ?? chainAnchors.easContract;
3295
+ const provider = params.provider;
3296
+ const controllerWitnessSchemaUid = chainAnchors.schemas["controller-witness"];
3297
+ const keyBindingSchemaUid = chainAnchors.schemas["key-binding"];
3298
+ const subjectAddress = didToAddress(params.subjectDid);
3299
+ let relevantWitnesses = [];
3300
+ if (controllerWitnessSchemaUid) {
3301
+ const rawWitnesses = await queryByRecipientAndSchema(
3302
+ easContractAddress,
3303
+ provider,
3304
+ subjectAddress,
3305
+ [controllerWitnessSchemaUid],
3306
+ params.fromBlock ?? 0
3307
+ );
3308
+ relevantWitnesses = rawWitnesses.filter((att) => {
3309
+ const witnessController = att.data?.controller;
3310
+ if (typeof witnessController !== "string") return false;
3311
+ return isSameControllerId(witnessController, controllerDid);
3312
+ });
3313
+ relevantWitnesses.sort((a, b) => Number(a.time - b.time));
3314
+ }
3315
+ const controllerWitnesses = relevantWitnesses.map((att) => ({
3316
+ uid: att.uid,
3317
+ issuedAt: att.time,
3318
+ attester: att.attester,
3319
+ method: resolveWitnessMethod(att.data?.method)
3320
+ }));
3321
+ let keyBindingUid = null;
3322
+ let until = null;
3323
+ let keyPurposeStatus = "not-required";
3324
+ if (keyBindingSchemaUid) {
3325
+ const keyBindings = await queryByRecipientAndSchema(
3326
+ easContractAddress,
3327
+ provider,
3328
+ subjectAddress,
3329
+ [keyBindingSchemaUid],
3330
+ params.fromBlock ?? 0
3331
+ );
3332
+ const relevantKeyBindings = keyBindings.filter((att) => controllerMatchesKeyBinding(att, controllerDid, controllerMethod)).sort((a, b) => Number(b.time - a.time));
3333
+ const relevantKeyBinding = relevantKeyBindings[0] ?? null;
3334
+ if (relevantKeyBinding) {
3335
+ keyBindingUid = relevantKeyBinding.uid;
3336
+ if (relevantKeyBinding.revocationTime > 0n) {
3337
+ until = relevantKeyBinding.revocationTime;
3338
+ }
3339
+ const keyPurposes = relevantKeyBinding.data?.keyPurpose;
3340
+ if (!Array.isArray(keyPurposes) || keyPurposes.length === 0) {
3341
+ keyPurposeStatus = "unknown";
3342
+ } else {
3343
+ const allMatched = purposes.every((p) => keyPurposes.includes(p));
3344
+ keyPurposeStatus = allMatched ? "matched" : "mismatch";
3345
+ }
3346
+ }
3347
+ }
3348
+ let currentlyVerified = false;
3349
+ let liveMethod = null;
3350
+ let transferProofVerified = false;
3351
+ let transferProofAnchor = null;
3352
+ const subjectMethod = extractDidMethod(params.subjectDid);
3353
+ if (subjectMethod === "web") {
3354
+ const domain = getDomainFromDidWeb(params.subjectDid);
3355
+ if (domain) {
3356
+ try {
3357
+ const dnsResult = await verifyDnsTxtControllerDid(domain, controllerDid, {
3358
+ resolveTxt: params.resolveTxt
3359
+ });
3360
+ if (dnsResult.valid) {
3361
+ currentlyVerified = true;
3362
+ liveMethod = "dns";
3363
+ }
3364
+ } catch {
3365
+ }
3366
+ if (!currentlyVerified) {
3367
+ try {
3368
+ const fetchDoc = params.fetchDidDocument ?? fetchDidDocument;
3369
+ const didDoc = await fetchDoc(domain);
3370
+ const didJsonResult = verifyDidDocumentControllerDid(didDoc, controllerDid);
3371
+ if (didJsonResult.valid) {
3372
+ currentlyVerified = true;
3373
+ liveMethod = "did-document";
3374
+ }
3375
+ } catch {
3376
+ }
3377
+ }
3378
+ }
3379
+ } else if (subjectMethod === "pkh") {
3380
+ if (isEvmDidPkh(params.subjectDid) && controllerEvmAddress) {
3381
+ const subjectContractAddress = getAddressFromDidPkh(params.subjectDid);
3382
+ const subjectChainId = getChainIdFromDidPkh(params.subjectDid);
3383
+ const chainIdNum = subjectChainId ? Number(subjectChainId) : null;
3384
+ if (subjectContractAddress && chainIdNum) {
3385
+ const ownerAddress = await discoverContractOwner(
3386
+ provider,
3387
+ subjectContractAddress
3388
+ );
3389
+ if (ownerAddress && getAddress(ownerAddress) === getAddress(controllerEvmAddress)) {
3390
+ currentlyVerified = true;
3391
+ liveMethod = "contract-ownership";
3392
+ }
3393
+ if (!currentlyVerified && keyBindingUid) {
3394
+ const relevantKeyBinding = await findKeyBindingWithTransferProof(
3395
+ keyBindingSchemaUid,
3396
+ easContractAddress,
3397
+ provider,
3398
+ subjectAddress,
3399
+ controllerDid,
3400
+ controllerMethod,
3401
+ params.fromBlock ?? 0
3402
+ );
3403
+ if (relevantKeyBinding) {
3404
+ const proofTxHash = relevantKeyBinding.data?.proofTxHash;
3405
+ if (proofTxHash && /^0x[0-9a-fA-F]{64}$/.test(proofTxHash)) {
3406
+ const verified = await verifyTransferProof(
3407
+ provider,
3408
+ proofTxHash,
3409
+ params.subjectDid,
3410
+ controllerEvmAddress,
3411
+ chainIdNum
3412
+ );
3413
+ if (verified) {
3414
+ transferProofVerified = true;
3415
+ transferProofAnchor = relevantKeyBinding.time;
3416
+ }
3417
+ }
3418
+ }
3419
+ }
3420
+ }
3421
+ }
3422
+ }
3423
+ const witnessAnchor = relevantWitnesses.length > 0 ? relevantWitnesses[0].time : null;
3424
+ const anchoredFrom = witnessAnchor ?? transferProofAnchor;
3425
+ const purposeBlocks = keyPurposeStatus === "mismatch";
3426
+ const hasOpenWindow = relevantWitnesses.length > 0 && until === null;
3427
+ const hasKeyBindingWithProof = keyBindingUid !== null && transferProofVerified && until === null;
3428
+ const authorized = !purposeBlocks && (hasOpenWindow || currentlyVerified && until === null || hasKeyBindingWithProof);
3429
+ return {
3430
+ authorized,
3431
+ anchoredFrom,
3432
+ until,
3433
+ currentlyVerified,
3434
+ liveMethod,
3435
+ controllerWitnesses,
3436
+ keyBindingUid,
3437
+ keyPurposeStatus,
3438
+ transferProofVerified: transferProofVerified || void 0
3439
+ };
3440
+ }
3441
+ function controllerMatchesKeyBinding(att, controllerDid, controllerMethod) {
3442
+ const keyId = att.data?.keyId;
3443
+ if (typeof keyId === "string") {
3444
+ if (isSameControllerId(keyId, controllerDid)) {
3445
+ return true;
3446
+ }
3447
+ }
3448
+ if (controllerMethod === "jwk") {
3449
+ const publicKeyJwkRaw = att.data?.publicKeyJwk;
3450
+ if (publicKeyJwkRaw) {
3451
+ try {
3452
+ const onChainJwk = typeof publicKeyJwkRaw === "string" ? JSON.parse(publicKeyJwkRaw) : publicKeyJwkRaw;
3453
+ const controllerJwk = didJwkToJwk(controllerDid);
3454
+ return publicJwkEquals(onChainJwk, controllerJwk);
3455
+ } catch {
3456
+ }
3457
+ }
3458
+ }
3459
+ return false;
3460
+ }
3461
+ var EAS_EVENT_ABI2 = [
3462
+ "event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
3463
+ ];
3464
+ function resolveWitnessMethod(value) {
3465
+ if (typeof value !== "string") return void 0;
3466
+ switch (value) {
3467
+ case "dns":
3468
+ case "dns-txt":
3469
+ return "dns";
3470
+ case "did-document":
3471
+ case "did-json":
3472
+ return "did-document";
3473
+ case "manual":
3474
+ return "manual";
3475
+ default:
3476
+ return "other";
3477
+ }
1683
3478
  }
1684
- async function callControllerWitness(params) {
1685
- const dnsResult = await callMethod(params, "dns-txt").catch(() => null);
1686
- if (dnsResult) {
1687
- return dnsResult;
3479
+ async function queryByRecipientAndSchema(easContractAddress, provider, recipient, schemas, fromBlock) {
3480
+ const contract = new Contract(easContractAddress, EAS_EVENT_ABI2, provider);
3481
+ const filter = contract.filters.Attested(recipient, null);
3482
+ const toBlock = await provider.getBlockNumber();
3483
+ let events;
3484
+ try {
3485
+ events = await contract.queryFilter(filter, fromBlock, toBlock);
3486
+ } catch (err) {
3487
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to query attestation events", { err });
1688
3488
  }
1689
- const didJsonResult = await callMethod(params, "did-json").catch(() => null);
1690
- if (didJsonResult) {
1691
- return didJsonResult;
3489
+ const eas = new EAS(easContractAddress);
3490
+ eas.connect(provider);
3491
+ const schemaFilter = schemas.map((s) => s.toLowerCase());
3492
+ const results = [];
3493
+ for (const event of events) {
3494
+ if (!("args" in event) || !Array.isArray(event.args)) continue;
3495
+ const args = event.args;
3496
+ const uid = args?.[2];
3497
+ const schemaUid = args?.[3];
3498
+ if (!uid || !schemaUid) continue;
3499
+ if (!schemaFilter.includes(schemaUid.toLowerCase())) continue;
3500
+ const attestation = await eas.getAttestation(uid);
3501
+ if (!attestation || !attestation.uid) continue;
3502
+ let data = {};
3503
+ const rawData = attestation.data;
3504
+ if (rawData && rawData !== "0x") {
3505
+ try {
3506
+ data = decodeAttestationData(
3507
+ "string subject, string controller, string method",
3508
+ rawData
3509
+ );
3510
+ } catch {
3511
+ try {
3512
+ data = decodeAttestationData(
3513
+ "string subject, string keyId, string publicKeyJwk, string[] keyPurpose, string[] proofs, uint256 issuedAt, uint256 effectiveAt, uint256 expiresAt",
3514
+ rawData
3515
+ );
3516
+ } catch {
3517
+ }
3518
+ }
3519
+ }
3520
+ const toBigIntSafe = (value) => {
3521
+ if (typeof value === "bigint") return value;
3522
+ if (typeof value === "number") return BigInt(value);
3523
+ if (typeof value === "string" && value.length > 0) return BigInt(value);
3524
+ return 0n;
3525
+ };
3526
+ results.push({
3527
+ uid: attestation.uid,
3528
+ schema: attestation.schema,
3529
+ attester: attestation.attester,
3530
+ recipient: attestation.recipient,
3531
+ revocable: Boolean(attestation.revocable),
3532
+ revocationTime: toBigIntSafe(attestation.revocationTime),
3533
+ expirationTime: toBigIntSafe(attestation.expirationTime),
3534
+ time: toBigIntSafe(attestation.time),
3535
+ refUID: attestation.refUID,
3536
+ data
3537
+ });
1692
3538
  }
1693
- return {
1694
- ok: false,
1695
- method: "did-json"
1696
- };
3539
+ return results;
3540
+ }
3541
+ async function findKeyBindingWithTransferProof(keyBindingSchemaUid, easContractAddress, provider, subjectAddress, controllerDid, controllerMethod, fromBlock) {
3542
+ const keyBindings = await queryByRecipientAndSchema(
3543
+ easContractAddress,
3544
+ provider,
3545
+ subjectAddress,
3546
+ [keyBindingSchemaUid],
3547
+ fromBlock
3548
+ );
3549
+ const withProof = keyBindings.filter((att) => {
3550
+ if (!controllerMatchesKeyBinding(att, controllerDid, controllerMethod)) return false;
3551
+ const proofs = att.data?.proofs;
3552
+ if (Array.isArray(proofs)) {
3553
+ return proofs.some((p) => typeof p === "string" && /^0x[0-9a-fA-F]{64}$/.test(p));
3554
+ }
3555
+ return typeof att.data?.proofTxHash === "string";
3556
+ }).sort((a, b) => Number(b.time - a.time));
3557
+ return withProof[0] ?? null;
1697
3558
  }
1698
3559
 
1699
3560
  // src/reputation/proof/tx-interaction.ts
@@ -1803,6 +3664,42 @@ function createX402OfferProof(offer) {
1803
3664
  };
1804
3665
  }
1805
3666
 
3667
+ // src/reputation/proof/x402-verify.ts
3668
+ async function verifyX402Artifact(artifact, options) {
3669
+ if (!artifact || typeof artifact !== "object") {
3670
+ return {
3671
+ valid: false,
3672
+ error: { code: "INVALID_ARTIFACT", message: "Artifact must be a non-null object" }
3673
+ };
3674
+ }
3675
+ const format = artifact.format;
3676
+ switch (format) {
3677
+ case "jws": {
3678
+ const jwsArtifact = artifact;
3679
+ const jwsOptions = options.resolveOptions ? { resolveOptions: options.resolveOptions } : void 0;
3680
+ if (options.artifactType === "offer") {
3681
+ return verifyX402JwsOffer(jwsArtifact, jwsOptions);
3682
+ }
3683
+ return verifyX402JwsReceipt(jwsArtifact, jwsOptions);
3684
+ }
3685
+ case "eip712": {
3686
+ const eip712Artifact = artifact;
3687
+ if (options.artifactType === "offer") {
3688
+ return verifyX402Eip712Offer(eip712Artifact);
3689
+ }
3690
+ return verifyX402Eip712Receipt(eip712Artifact);
3691
+ }
3692
+ default:
3693
+ return {
3694
+ valid: false,
3695
+ error: {
3696
+ code: "UNSUPPORTED_FORMAT",
3697
+ message: `Unsupported x402 artifact format: "${format}"`
3698
+ }
3699
+ };
3700
+ }
3701
+ }
3702
+
1806
3703
  // src/reputation/proof/evidence-pointer.ts
1807
3704
  function createEvidencePointerProof(url) {
1808
3705
  if (!url || typeof url !== "string") {
@@ -1816,63 +3713,12 @@ function createEvidencePointerProof(url) {
1816
3713
  issuedAt: Math.floor(Date.now() / 1e3)
1817
3714
  };
1818
3715
  }
1819
- async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options = {}) {
1820
- if (!domain || typeof domain !== "string") {
1821
- throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1822
- }
1823
- const expected = normalizeDid(expectedControllerDid);
1824
- const prefix = options.recordPrefix ?? "_controllers";
1825
- const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
1826
- let records;
1827
- try {
1828
- records = await (options.resolveTxt ?? resolveTxt)(host);
1829
- } catch (err) {
1830
- throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
1831
- }
1832
- for (const recordParts of records) {
1833
- const record = recordParts.join("");
1834
- const parsed = parseDnsTxtRecord(record);
1835
- if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
1836
- return { valid: true, record };
1837
- }
1838
- }
1839
- return { valid: false, reason: "No TXT record matched expected controller DID" };
1840
- }
1841
-
1842
- // src/reputation/proof/dns-txt-shared.ts
1843
- async function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options = {}) {
1844
- if (!domain || typeof domain !== "string") {
1845
- throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1846
- }
1847
- if (!options.resolveTxt) {
1848
- throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
1849
- }
1850
- const expected = normalizeDid(expectedControllerDid);
1851
- const prefix = options.recordPrefix ?? "_controllers";
1852
- const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
1853
- let records;
1854
- try {
1855
- records = await options.resolveTxt(host);
1856
- } catch (err) {
1857
- throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
1858
- }
1859
- for (const recordParts of records) {
1860
- const record = recordParts.join("");
1861
- const parsed = parseDnsTxtRecord(record);
1862
- if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
1863
- return { valid: true, record };
1864
- }
1865
- }
1866
- return { valid: false, reason: "No TXT record matched expected controller DID" };
3716
+ function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options = {}) {
3717
+ return verifyDnsTxtControllerDid(domain, expectedControllerDid, {
3718
+ ...options,
3719
+ resolveTxt: options.resolveTxt ?? resolveTxt
3720
+ });
1867
3721
  }
1868
-
1869
- // src/reputation/proof/subject-ownership.ts
1870
- var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
1871
- var OWNERSHIP_PATTERNS = [
1872
- { method: "owner", signature: "function owner() view returns (address)" },
1873
- { method: "admin", signature: "function admin() view returns (address)" },
1874
- { method: "getOwner", signature: "function getOwner() view returns (address)" }
1875
- ];
1876
3722
  function assertConnectedWalletDid(input) {
1877
3723
  const normalized = normalizeDid(input);
1878
3724
  if (extractDidMethod(normalized) !== "pkh") {
@@ -1885,43 +3731,6 @@ function assertConnectedWalletDid(input) {
1885
3731
  function normalizeSubjectDid(input) {
1886
3732
  return normalizeDid(input);
1887
3733
  }
1888
- async function readAddressFromContract(provider, contractAddress, signature, method) {
1889
- try {
1890
- const iface = new Interface([signature]);
1891
- const data = iface.encodeFunctionData(method, []);
1892
- const result = await provider.call({ to: contractAddress, data });
1893
- const [value] = iface.decodeFunctionResult(method, result);
1894
- if (typeof value === "string" && isAddress(value) && value !== ZeroAddress) {
1895
- return getAddress(value);
1896
- }
1897
- } catch {
1898
- }
1899
- return null;
1900
- }
1901
- async function discoverControllingWallet(provider, contractAddress, chainId) {
1902
- for (const pattern of OWNERSHIP_PATTERNS) {
1903
- const address = await readAddressFromContract(
1904
- provider,
1905
- contractAddress,
1906
- pattern.signature,
1907
- pattern.method
1908
- );
1909
- if (address) {
1910
- return buildEvmDidPkh(chainId, address);
1911
- }
1912
- }
1913
- try {
1914
- const adminValue = await provider.getStorage(contractAddress, EIP1967_ADMIN_SLOT);
1915
- if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1916
- const adminAddress = getAddress(`0x${adminValue.slice(-40)}`);
1917
- if (adminAddress !== ZeroAddress) {
1918
- return buildEvmDidPkh(chainId, adminAddress);
1919
- }
1920
- }
1921
- } catch {
1922
- }
1923
- return null;
1924
- }
1925
3734
  async function verifyDidWebOwnership(params) {
1926
3735
  const subjectDid = normalizeSubjectDid(params.subjectDid);
1927
3736
  const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
@@ -1933,7 +3742,7 @@ async function verifyDidWebOwnership(params) {
1933
3742
  }
1934
3743
  let dnsReason;
1935
3744
  try {
1936
- const dnsResult = await verifyDnsTxtControllerDid2(domain, connectedWalletDid, {
3745
+ const dnsResult = await verifyDnsTxtControllerDid(domain, connectedWalletDid, {
1937
3746
  resolveTxt: params.resolveTxt,
1938
3747
  recordPrefix: params.recordPrefix
1939
3748
  });
@@ -1999,8 +3808,8 @@ async function verifyDidPkhOwnership(params) {
1999
3808
  subjectDid: params.subjectDid
2000
3809
  });
2001
3810
  }
2002
- const code = await params.provider.getCode(subjectAddress);
2003
- const isContract = code !== "0x" && code !== "0x0";
3811
+ const code2 = await params.provider.getCode(subjectAddress);
3812
+ const isContract = code2 !== "0x" && code2 !== "0x0";
2004
3813
  if (!isContract) {
2005
3814
  if (getAddress(subjectAddress) === getAddress(connectedWalletAddress)) {
2006
3815
  return {
@@ -2019,7 +3828,7 @@ async function verifyDidPkhOwnership(params) {
2019
3828
  connectedWalletDid
2020
3829
  };
2021
3830
  }
2022
- const controllingWalletDid = await discoverControllingWallet(params.provider, subjectAddress, chainId);
3831
+ const controllingWalletDid = await discoverControllingWalletDid(params.provider, subjectAddress, chainId);
2023
3832
  if (params.txHash) {
2024
3833
  if (!controllingWalletDid) {
2025
3834
  return {
@@ -2102,7 +3911,7 @@ async function verifyDidPkhOwnership(params) {
2102
3911
  };
2103
3912
  }
2104
3913
  for (const pattern of OWNERSHIP_PATTERNS) {
2105
- const ownerAddress = await readAddressFromContract(
3914
+ const ownerAddress = await readOwnerFromContract(
2106
3915
  params.provider,
2107
3916
  subjectAddress,
2108
3917
  pattern.signature,
@@ -2176,6 +3985,300 @@ async function verifySubjectOwnership(params) {
2176
3985
  subjectDid
2177
3986
  });
2178
3987
  }
3988
+ function extractEip712Signer(proof) {
3989
+ const { domain, message, signature } = proof.proofObject;
3990
+ const typedData = {
3991
+ domain,
3992
+ types: {
3993
+ OmaTrustProof: [
3994
+ { name: "signer", type: "address" },
3995
+ { name: "authorizedEntity", type: "string" },
3996
+ { name: "signingPurpose", type: "string" },
3997
+ { name: "creationTimestamp", type: "uint256" },
3998
+ { name: "expirationTimestamp", type: "uint256" },
3999
+ { name: "randomValue", type: "bytes32" },
4000
+ { name: "statement", type: "string" }
4001
+ ]
4002
+ },
4003
+ message
4004
+ };
4005
+ try {
4006
+ const result = verifyEip712Signature(typedData, signature);
4007
+ return result.valid && result.signer ? result.signer : null;
4008
+ } catch (err) {
4009
+ console.warn("[omatrust] EIP-712 signer extraction failed:", err);
4010
+ return null;
4011
+ }
4012
+ }
4013
+ function extractJwsHeaderJwk(proof) {
4014
+ const jws = proof.proofObject;
4015
+ if (typeof jws !== "string") return null;
4016
+ const parts = jws.split(".");
4017
+ if (parts.length !== 3) return null;
4018
+ try {
4019
+ const headerB64 = parts[0].replace(/-/g, "+").replace(/_/g, "/");
4020
+ const padded = headerB64.padEnd(
4021
+ headerB64.length + (4 - headerB64.length % 4) % 4,
4022
+ "="
4023
+ );
4024
+ let headerJson;
4025
+ if (typeof atob === "function") {
4026
+ headerJson = decodeURIComponent(
4027
+ Array.from(atob(padded)).map((char) => `%${char.charCodeAt(0).toString(16).padStart(2, "0")}`).join("")
4028
+ );
4029
+ } else {
4030
+ headerJson = Buffer.from(padded, "base64").toString("utf8");
4031
+ }
4032
+ const header = JSON.parse(headerJson);
4033
+ return header.jwk ?? null;
4034
+ } catch {
4035
+ return null;
4036
+ }
4037
+ }
4038
+ function extractJwsPayload(proof) {
4039
+ const jws = proof.proofObject;
4040
+ if (typeof jws !== "string") return null;
4041
+ const parts = jws.split(".");
4042
+ if (parts.length !== 3) return null;
4043
+ try {
4044
+ const payloadB64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
4045
+ const padded = payloadB64.padEnd(
4046
+ payloadB64.length + (4 - payloadB64.length % 4) % 4,
4047
+ "="
4048
+ );
4049
+ let payloadJson;
4050
+ if (typeof atob === "function") {
4051
+ payloadJson = decodeURIComponent(
4052
+ Array.from(atob(padded)).map((char) => `%${char.charCodeAt(0).toString(16).padStart(2, "0")}`).join("")
4053
+ );
4054
+ } else {
4055
+ payloadJson = Buffer.from(padded, "base64").toString("utf8");
4056
+ }
4057
+ return JSON.parse(payloadJson);
4058
+ } catch {
4059
+ return null;
4060
+ }
4061
+ }
4062
+ function signerMatchesDid(signerAddress, did) {
4063
+ try {
4064
+ const didAddress = extractAddressFromDid(did);
4065
+ return getAddress(signerAddress).toLowerCase() === getAddress(didAddress).toLowerCase();
4066
+ } catch {
4067
+ return false;
4068
+ }
4069
+ }
4070
+ function jwkMatchesDid(jwk, did) {
4071
+ const method = extractDidMethod(did);
4072
+ if (method === "jwk") {
4073
+ try {
4074
+ const didJwk = didJwkToJwk(did);
4075
+ return publicJwkEquals(jwk, didJwk);
4076
+ } catch {
4077
+ return false;
4078
+ }
4079
+ }
4080
+ return false;
4081
+ }
4082
+ function proofSignerMatchesDid(proof, did) {
4083
+ switch (proof.proofType) {
4084
+ case "pop-eip712": {
4085
+ const signer = extractEip712Signer(proof);
4086
+ if (!signer) return false;
4087
+ return signerMatchesDid(signer, did);
4088
+ }
4089
+ case "pop-jws": {
4090
+ const jwsProof = proof;
4091
+ const jwk = extractJwsHeaderJwk(jwsProof);
4092
+ if (jwk && jwkMatchesDid(jwk, did)) return true;
4093
+ const payload = extractJwsPayload(jwsProof);
4094
+ if (payload && typeof payload.iss === "string") {
4095
+ if (payload.iss === did) return true;
4096
+ }
4097
+ return false;
4098
+ }
4099
+ case "tx-encoded-value":
4100
+ case "tx-interaction":
4101
+ return false;
4102
+ case "evidence-pointer":
4103
+ return false;
4104
+ default:
4105
+ return false;
4106
+ }
4107
+ }
4108
+ function proofAuthorizedEntityMatchesDid(proof, did) {
4109
+ switch (proof.proofType) {
4110
+ case "pop-eip712": {
4111
+ const eip712 = proof;
4112
+ const authorizedEntity = eip712.proofObject.message.authorizedEntity;
4113
+ return typeof authorizedEntity === "string" && authorizedEntity === did;
4114
+ }
4115
+ case "pop-jws": {
4116
+ const payload = extractJwsPayload(proof);
4117
+ if (!payload) return false;
4118
+ return typeof payload.aud === "string" && payload.aud === did;
4119
+ }
4120
+ default:
4121
+ return false;
4122
+ }
4123
+ }
4124
+ function verifyLinkedIdentifierProofs(data) {
4125
+ if (!data.subject || !data.linkedId) {
4126
+ return {
4127
+ valid: false,
4128
+ checks: [],
4129
+ reasons: ["subject and linkedId are required"]
4130
+ };
4131
+ }
4132
+ if (!Array.isArray(data.proofs) || data.proofs.length === 0) {
4133
+ return {
4134
+ valid: false,
4135
+ checks: [],
4136
+ reasons: ["At least one proof is required"]
4137
+ };
4138
+ }
4139
+ const checks = [];
4140
+ let subjectProved = false;
4141
+ let linkedIdProved = false;
4142
+ for (let i = 0; i < data.proofs.length; i++) {
4143
+ const proof = data.proofs[i];
4144
+ if (proofSignerMatchesDid(proof, data.subject)) {
4145
+ checks.push({
4146
+ proofIndex: i,
4147
+ proofType: proof.proofType,
4148
+ checkType: "signer-is-subject",
4149
+ valid: true
4150
+ });
4151
+ subjectProved = true;
4152
+ if (!proofAuthorizedEntityMatchesDid(proof, data.linkedId)) {
4153
+ checks.push({
4154
+ proofIndex: i,
4155
+ proofType: proof.proofType,
4156
+ checkType: "signer-is-subject",
4157
+ valid: false,
4158
+ reason: "Proof from subject does not authorize linkedId (aud mismatch)"
4159
+ });
4160
+ }
4161
+ }
4162
+ if (proofSignerMatchesDid(proof, data.linkedId)) {
4163
+ checks.push({
4164
+ proofIndex: i,
4165
+ proofType: proof.proofType,
4166
+ checkType: "signer-is-linkedId",
4167
+ valid: true
4168
+ });
4169
+ linkedIdProved = true;
4170
+ if (!proofAuthorizedEntityMatchesDid(proof, data.subject)) {
4171
+ checks.push({
4172
+ proofIndex: i,
4173
+ proofType: proof.proofType,
4174
+ checkType: "signer-is-linkedId",
4175
+ valid: false,
4176
+ reason: "Proof from linkedId does not authorize subject (aud mismatch)"
4177
+ });
4178
+ }
4179
+ }
4180
+ if (proof.proofType === "evidence-pointer") {
4181
+ checks.push({
4182
+ proofIndex: i,
4183
+ proofType: proof.proofType,
4184
+ checkType: "signer-is-subject",
4185
+ valid: true,
4186
+ reason: "Evidence pointer accepted (requires URL fetch for full verification)"
4187
+ });
4188
+ subjectProved = true;
4189
+ }
4190
+ }
4191
+ const reasons = [];
4192
+ if (!subjectProved) {
4193
+ reasons.push("No proof demonstrates control by subject");
4194
+ }
4195
+ if (!linkedIdProved) {
4196
+ reasons.push("No proof demonstrates control by linkedId");
4197
+ }
4198
+ return {
4199
+ valid: reasons.length === 0,
4200
+ checks,
4201
+ reasons
4202
+ };
4203
+ }
4204
+ function verifyKeyBindingProofs(data) {
4205
+ if (!data.subject || !data.keyId) {
4206
+ return {
4207
+ valid: false,
4208
+ checks: [],
4209
+ reasons: ["subject and keyId are required"]
4210
+ };
4211
+ }
4212
+ if (!Array.isArray(data.proofs) || data.proofs.length === 0) {
4213
+ return {
4214
+ valid: false,
4215
+ checks: [],
4216
+ reasons: ["At least one proof is required"]
4217
+ };
4218
+ }
4219
+ const checks = [];
4220
+ let subjectAuthorizedKey = false;
4221
+ for (let i = 0; i < data.proofs.length; i++) {
4222
+ const proof = data.proofs[i];
4223
+ if (proofSignerMatchesDid(proof, data.subject)) {
4224
+ const authorizesKey = proofAuthorizedEntityMatchesDid(proof, data.keyId);
4225
+ checks.push({
4226
+ proofIndex: i,
4227
+ proofType: proof.proofType,
4228
+ checkType: "signer-is-subject-for-key",
4229
+ valid: authorizesKey,
4230
+ reason: authorizesKey ? void 0 : "Proof signed by subject but does not authorize the keyId"
4231
+ });
4232
+ if (authorizesKey) {
4233
+ subjectAuthorizedKey = true;
4234
+ }
4235
+ }
4236
+ if (proofSignerMatchesDid(proof, data.keyId)) {
4237
+ checks.push({
4238
+ proofIndex: i,
4239
+ proofType: proof.proofType,
4240
+ checkType: "signer-is-keyId",
4241
+ valid: true,
4242
+ reason: "Key proved possession (supplementary, not sufficient alone)"
4243
+ });
4244
+ }
4245
+ if (proof.proofType === "evidence-pointer") {
4246
+ checks.push({
4247
+ proofIndex: i,
4248
+ proofType: proof.proofType,
4249
+ checkType: "signer-is-subject-for-key",
4250
+ valid: true,
4251
+ reason: "Evidence pointer accepted (requires URL fetch for full verification)"
4252
+ });
4253
+ subjectAuthorizedKey = true;
4254
+ }
4255
+ }
4256
+ if (data.publicKeyJwk && extractDidMethod(data.keyId) === "jwk") {
4257
+ try {
4258
+ const keyIdJwk = didJwkToJwk(data.keyId);
4259
+ const jwkConsistent = publicJwkEquals(data.publicKeyJwk, keyIdJwk);
4260
+ if (!jwkConsistent) {
4261
+ return {
4262
+ valid: false,
4263
+ checks,
4264
+ reasons: ["publicKeyJwk does not match the key material in keyId (did:jwk)"]
4265
+ };
4266
+ }
4267
+ } catch {
4268
+ }
4269
+ }
4270
+ const reasons = [];
4271
+ if (!subjectAuthorizedKey) {
4272
+ reasons.push(
4273
+ "No proof demonstrates that subject authorized the key binding"
4274
+ );
4275
+ }
4276
+ return {
4277
+ valid: reasons.length === 0,
4278
+ checks,
4279
+ reasons
4280
+ };
4281
+ }
2179
4282
 
2180
4283
  // src/app-registry/index.ts
2181
4284
  var app_registry_exports = {};
@@ -2263,8 +4366,8 @@ function getInterfaceTypes(bitmap) {
2263
4366
  }
2264
4367
 
2265
4368
  // src/app-registry/status.ts
2266
- function registryCodeToStatus(code) {
2267
- switch (code) {
4369
+ function registryCodeToStatus(code2) {
4370
+ switch (code2) {
2268
4371
  case 0:
2269
4372
  return "Active";
2270
4373
  case 1:
@@ -2272,7 +4375,7 @@ function registryCodeToStatus(code) {
2272
4375
  case 2:
2273
4376
  return "Replaced";
2274
4377
  default:
2275
- throw new OmaTrustError("INVALID_INPUT", "Invalid registry status code", { code });
4378
+ throw new OmaTrustError("INVALID_INPUT", "Invalid registry status code", { code: code2 });
2276
4379
  }
2277
4380
  }
2278
4381
  function registryStatusToCode(status) {
@@ -2374,6 +4477,9 @@ async function computeDataHashFromUrl(url, algorithm) {
2374
4477
  if (!url || typeof url !== "string") {
2375
4478
  throw new OmaTrustError("INVALID_INPUT", "url must be a non-empty string", { url });
2376
4479
  }
4480
+ if (algorithm !== "keccak256" && algorithm !== "sha256") {
4481
+ throw new OmaTrustError("INVALID_INPUT", `Unsupported hash algorithm: "${algorithm}". Must be "keccak256" or "sha256".`, { algorithm });
4482
+ }
2377
4483
  const json = await fetchJson(url);
2378
4484
  const jcsJson = canonicalizeJson(json);
2379
4485
  return hashJcs(jcsJson, algorithm);