@kya-os/verifier 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.js ADDED
@@ -0,0 +1,678 @@
1
+ import { importJWK, jwtVerify } from "jose";
2
+ import { AGENT_HEADERS, ERROR_HTTP_STATUS } from "@kya-os/contracts/verifier";
3
+ /**
4
+ * Isomorphic verifier core for XMCP-I proof validation
5
+ *
6
+ * This is the heart of the trust system - it verifies that AI agents
7
+ * are who they claim to be and have the authority to perform actions.
8
+ */
9
+ export class VerifierCore {
10
+ constructor(config = {}) {
11
+ this.didCache = new Map();
12
+ this.delegationCache = new Map();
13
+ this.config = {
14
+ ktaBaseUrl: config.ktaBaseUrl || "https://knowthat.ai",
15
+ enableDelegationCheck: config.enableDelegationCheck ?? true,
16
+ clockSkewTolerance: config.clockSkewTolerance ?? 120,
17
+ sessionTimeout: config.sessionTimeout ?? 1800, // 30 minutes
18
+ allowMockData: config.allowMockData ?? false,
19
+ didCacheTtl: config.didCacheTtl ?? 300, // 5 minutes
20
+ delegationCacheTtl: config.delegationCacheTtl ?? 60, // 1 minute
21
+ };
22
+ }
23
+ /**
24
+ * Verify a detached proof and return verification result
25
+ *
26
+ * This is the main entry point for proof verification. It performs
27
+ * a comprehensive validation of the agent's identity and authorization.
28
+ */
29
+ async verify(context) {
30
+ const startTime = Date.now();
31
+ try {
32
+ // 1. Validate proof structure
33
+ const structureError = this.validateProofStructure(context.proof);
34
+ if (structureError) {
35
+ this.logVerificationAttempt(context, false, structureError.code);
36
+ return this.createErrorResult(structureError);
37
+ }
38
+ // 2. Verify timestamp and session validity
39
+ const timestampError = this.validateTimestamp(context.proof.meta, context.timestamp);
40
+ if (timestampError) {
41
+ this.logVerificationAttempt(context, false, timestampError.code);
42
+ return this.createErrorResult(timestampError);
43
+ }
44
+ // 3. Verify audience matches
45
+ const audienceError = this.validateAudience(context.proof.meta, context.audience);
46
+ if (audienceError) {
47
+ this.logVerificationAttempt(context, false, audienceError.code);
48
+ return this.createErrorResult(audienceError);
49
+ }
50
+ // 4. Verify Ed25519 signature
51
+ const signatureError = await this.verifySignature(context.proof);
52
+ if (signatureError) {
53
+ this.logVerificationAttempt(context, false, signatureError.code);
54
+ return this.createErrorResult(signatureError);
55
+ }
56
+ // 5. Check delegation if enabled and present
57
+ if (this.config.enableDelegationCheck &&
58
+ context.proof.meta.delegationRef) {
59
+ const delegationError = await this.verifyDelegation(context.proof.meta.delegationRef, context.proof.meta.did, context.proof.meta.kid);
60
+ if (delegationError) {
61
+ this.logVerificationAttempt(context, false, delegationError.code);
62
+ return this.createErrorResult(delegationError);
63
+ }
64
+ }
65
+ // 6. Generate trusted headers and context
66
+ const headers = this.generateHeaders(context.proof.meta);
67
+ const agentContext = this.generateAgentContext(context.proof.meta);
68
+ const duration = Date.now() - startTime;
69
+ this.logVerificationAttempt(context, true, "SUCCESS", duration);
70
+ return {
71
+ success: true,
72
+ headers,
73
+ agentContext,
74
+ };
75
+ }
76
+ catch (error) {
77
+ const duration = Date.now() - startTime;
78
+ this.logVerificationAttempt(context, false, "UNEXPECTED_ERROR", duration);
79
+ return this.createErrorResult({
80
+ code: "XMCP_I_EVERIFY",
81
+ message: error instanceof Error ? error.message : "Verification failed",
82
+ httpStatus: 500,
83
+ details: {
84
+ reason: "Unexpected error during verification",
85
+ remediation: "Check proof format and try again",
86
+ },
87
+ });
88
+ }
89
+ }
90
+ /**
91
+ * Validate proof structure with comprehensive checks
92
+ */
93
+ validateProofStructure(proof) {
94
+ if (!proof.jws || typeof proof.jws !== "string") {
95
+ return {
96
+ code: "XMCP_I_EBADPROOF",
97
+ message: "Invalid proof: missing or invalid JWS",
98
+ httpStatus: 400,
99
+ details: {
100
+ reason: "JWS field is required and must be a string",
101
+ expected: "string",
102
+ received: typeof proof.jws,
103
+ },
104
+ };
105
+ }
106
+ if (!proof.meta || typeof proof.meta !== "object") {
107
+ return {
108
+ code: "XMCP_I_EBADPROOF",
109
+ message: "Invalid proof: missing or invalid meta",
110
+ httpStatus: 400,
111
+ details: {
112
+ reason: "Meta field is required and must be an object",
113
+ expected: "object",
114
+ received: typeof proof.meta,
115
+ },
116
+ };
117
+ }
118
+ const requiredFields = [
119
+ "did",
120
+ "kid",
121
+ "ts",
122
+ "nonce",
123
+ "audience",
124
+ "sessionId",
125
+ "requestHash",
126
+ "responseHash",
127
+ ];
128
+ for (const field of requiredFields) {
129
+ if (!proof.meta[field]) {
130
+ return {
131
+ code: "XMCP_I_EBADPROOF",
132
+ message: `Invalid proof: missing required field '${field}'`,
133
+ httpStatus: 400,
134
+ details: {
135
+ reason: `Field '${field}' is required in proof meta`,
136
+ expected: "non-empty value",
137
+ received: proof.meta[field],
138
+ },
139
+ };
140
+ }
141
+ }
142
+ // Validate hash format
143
+ const hashRegex = /^sha256:[a-f0-9]{64}$/;
144
+ if (!hashRegex.test(proof.meta.requestHash)) {
145
+ return {
146
+ code: "XMCP_I_EBADPROOF",
147
+ message: "Invalid proof: malformed requestHash",
148
+ httpStatus: 400,
149
+ details: {
150
+ reason: "requestHash must be in format 'sha256:<64-char-hex>'",
151
+ expected: "sha256:[a-f0-9]{64}",
152
+ received: proof.meta.requestHash,
153
+ },
154
+ };
155
+ }
156
+ if (!hashRegex.test(proof.meta.responseHash)) {
157
+ return {
158
+ code: "XMCP_I_EBADPROOF",
159
+ message: "Invalid proof: malformed responseHash",
160
+ httpStatus: 400,
161
+ details: {
162
+ reason: "responseHash must be in format 'sha256:<64-char-hex>'",
163
+ expected: "sha256:[a-f0-9]{64}",
164
+ received: proof.meta.responseHash,
165
+ },
166
+ };
167
+ }
168
+ // Validate DID format
169
+ if (!proof.meta.did.startsWith("did:")) {
170
+ return {
171
+ code: "XMCP_I_EBADPROOF",
172
+ message: "Invalid proof: malformed DID",
173
+ httpStatus: 400,
174
+ details: {
175
+ reason: "DID must start with 'did:'",
176
+ expected: "did:*",
177
+ received: proof.meta.did,
178
+ },
179
+ };
180
+ }
181
+ return null;
182
+ }
183
+ /**
184
+ * Validate timestamp with configurable clock skew tolerance
185
+ */
186
+ validateTimestamp(meta, currentTimestamp) {
187
+ const now = currentTimestamp || Math.floor(Date.now() / 1000);
188
+ const skew = this.config.clockSkewTolerance;
189
+ if (meta.ts < now - skew || meta.ts > now + skew) {
190
+ return {
191
+ code: "XMCP_I_EHANDSHAKE",
192
+ message: "Invalid proof: timestamp outside acceptable range",
193
+ httpStatus: 401,
194
+ details: {
195
+ reason: `Timestamp ${meta.ts} is outside ±${skew}s window`,
196
+ expected: `${now - skew} to ${now + skew}`,
197
+ received: meta.ts,
198
+ remediation: "Check NTP sync; adjust XMCP_I_TS_SKEW_SEC if needed",
199
+ },
200
+ };
201
+ }
202
+ return null;
203
+ }
204
+ /**
205
+ * Validate audience matches expected value
206
+ */
207
+ validateAudience(meta, expectedAudience) {
208
+ if (meta.audience !== expectedAudience) {
209
+ return {
210
+ code: "XMCP_I_EHANDSHAKE",
211
+ message: "Invalid proof: audience mismatch",
212
+ httpStatus: 401,
213
+ details: {
214
+ reason: "Proof audience does not match request audience",
215
+ expected: expectedAudience,
216
+ received: meta.audience,
217
+ },
218
+ };
219
+ }
220
+ return null;
221
+ }
222
+ /**
223
+ * Verify Ed25519 signature using JOSE with proper detached JWS handling
224
+ *
225
+ * This is the cryptographic heart of the verification process.
226
+ * It ensures the proof was signed by the claimed identity.
227
+ */
228
+ async verifySignature(proof) {
229
+ try {
230
+ // For testing with mock data, skip actual signature verification
231
+ if (this.config.allowMockData && proof.meta.did.startsWith("did:test:")) {
232
+ // Parse JWS components for basic validation
233
+ const jwsParts = proof.jws.split(".");
234
+ if (jwsParts.length !== 3 || jwsParts[1] !== "") {
235
+ return {
236
+ code: "XMCP_I_EBADPROOF",
237
+ message: "Invalid detached JWS format",
238
+ httpStatus: 403,
239
+ details: {
240
+ reason: "Detached JWS must have format 'header..signature'",
241
+ expected: "header..signature",
242
+ received: `${jwsParts.length} parts, payload: '${jwsParts[1]}'`,
243
+ },
244
+ };
245
+ }
246
+ const [headerB64] = jwsParts;
247
+ const header = JSON.parse(Buffer.from(headerB64, "base64url").toString());
248
+ if (header.alg !== "EdDSA") {
249
+ return {
250
+ code: "XMCP_I_EBADPROOF",
251
+ message: "Invalid JWS: unsupported algorithm",
252
+ httpStatus: 403,
253
+ details: {
254
+ reason: "Only EdDSA algorithm is supported",
255
+ expected: "EdDSA",
256
+ received: header.alg,
257
+ },
258
+ };
259
+ }
260
+ if (header.kid !== proof.meta.kid) {
261
+ return {
262
+ code: "XMCP_I_EBADPROOF",
263
+ message: "Invalid JWS: key ID mismatch",
264
+ httpStatus: 403,
265
+ details: {
266
+ reason: "JWS header kid must match proof meta kid",
267
+ expected: proof.meta.kid,
268
+ received: header.kid,
269
+ },
270
+ };
271
+ }
272
+ // Mock signature verification passes for test DIDs
273
+ return null;
274
+ }
275
+ // Parse JWS components - detached JWS has format: header..signature
276
+ const jwsParts = proof.jws.split(".");
277
+ if (jwsParts.length !== 3 || jwsParts[1] !== "") {
278
+ return {
279
+ code: "XMCP_I_EBADPROOF",
280
+ message: "Invalid detached JWS format",
281
+ httpStatus: 403,
282
+ details: {
283
+ reason: "Detached JWS must have format 'header..signature'",
284
+ expected: "header..signature",
285
+ received: `${jwsParts.length} parts, payload: '${jwsParts[1]}'`,
286
+ },
287
+ };
288
+ }
289
+ const [headerB64, , signatureB64] = jwsParts;
290
+ // Parse and validate header
291
+ const header = JSON.parse(Buffer.from(headerB64, "base64url").toString());
292
+ if (header.alg !== "EdDSA") {
293
+ return {
294
+ code: "XMCP_I_EBADPROOF",
295
+ message: "Invalid JWS: unsupported algorithm",
296
+ httpStatus: 403,
297
+ details: {
298
+ reason: "Only EdDSA algorithm is supported",
299
+ expected: "EdDSA",
300
+ received: header.alg,
301
+ },
302
+ };
303
+ }
304
+ if (header.kid !== proof.meta.kid) {
305
+ return {
306
+ code: "XMCP_I_EBADPROOF",
307
+ message: "Invalid JWS: key ID mismatch",
308
+ httpStatus: 403,
309
+ details: {
310
+ reason: "JWS header kid must match proof meta kid",
311
+ expected: proof.meta.kid,
312
+ received: header.kid,
313
+ },
314
+ };
315
+ }
316
+ // Fetch public key from DID document with caching
317
+ const publicKey = await this.fetchPublicKeyWithCache(proof.meta.did, proof.meta.kid);
318
+ if (!publicKey) {
319
+ return {
320
+ code: "XMCP_I_EBADPROOF",
321
+ message: "Unable to resolve public key",
322
+ httpStatus: 403,
323
+ details: {
324
+ reason: "Could not fetch public key from DID document",
325
+ remediation: "Ensure DID document is accessible and contains the key",
326
+ },
327
+ };
328
+ }
329
+ // Create canonical payload for detached signature verification
330
+ // This MUST match the exact format used by the runtime
331
+ const canonicalPayload = this.createCanonicalPayload(proof.meta);
332
+ const payloadB64 = Buffer.from(canonicalPayload, "utf8").toString("base64url");
333
+ // Reconstruct complete JWS for verification
334
+ const completeJWS = `${headerB64}.${payloadB64}.${signatureB64}`;
335
+ // Verify signature using JOSE
336
+ const jwk = await importJWK(publicKey);
337
+ await jwtVerify(completeJWS, jwk, {
338
+ algorithms: ["EdDSA"],
339
+ });
340
+ return null;
341
+ }
342
+ catch (error) {
343
+ return {
344
+ code: "XMCP_I_EBADPROOF",
345
+ message: "Signature verification failed",
346
+ httpStatus: 403,
347
+ details: {
348
+ reason: error instanceof Error ? error.message : "Unknown error",
349
+ remediation: "Check proof signature and public key",
350
+ },
351
+ };
352
+ }
353
+ }
354
+ /**
355
+ * Create canonical payload that matches runtime implementation
356
+ * Uses JSON Canonicalization Scheme (JCS) for deterministic ordering
357
+ */
358
+ createCanonicalPayload(meta) {
359
+ // Create payload object with exact field ordering as runtime
360
+ const payload = {
361
+ audience: meta.audience,
362
+ nonce: meta.nonce,
363
+ requestHash: meta.requestHash,
364
+ responseHash: meta.responseHash,
365
+ sessionId: meta.sessionId,
366
+ ts: meta.ts,
367
+ };
368
+ // Add optional fields in canonical order
369
+ if (meta.scopeId) {
370
+ payload.scopeId = meta.scopeId;
371
+ }
372
+ if (meta.delegationRef) {
373
+ payload.delegationRef = meta.delegationRef;
374
+ }
375
+ // Use JSON.stringify with sorted keys for canonicalization
376
+ // This implements a simplified JCS - for production, consider using a proper JCS library
377
+ return JSON.stringify(payload, Object.keys(payload).sort());
378
+ }
379
+ /**
380
+ * Verify delegation status via KTA with caching
381
+ */
382
+ async verifyDelegation(delegationRef, did, keyId) {
383
+ try {
384
+ // Check cache first
385
+ const cached = this.delegationCache.get(delegationRef);
386
+ if (cached && cached.expiresAt > Date.now()) {
387
+ return this.validateDelegationResponse(cached.response, did, keyId);
388
+ }
389
+ if (this.config.allowMockData) {
390
+ // Mock delegation for testing
391
+ if (delegationRef.startsWith("mock:")) {
392
+ const mockResponse = {
393
+ active: delegationRef !== "mock:revoked",
394
+ did,
395
+ keyId,
396
+ scopes: ["*"],
397
+ };
398
+ return this.validateDelegationResponse(mockResponse, did, keyId);
399
+ }
400
+ // For other delegation refs in test mode, assume they're valid
401
+ const mockResponse = {
402
+ active: true,
403
+ did,
404
+ keyId,
405
+ scopes: ["*"],
406
+ };
407
+ return this.validateDelegationResponse(mockResponse, did, keyId);
408
+ }
409
+ const response = await fetch(`${this.config.ktaBaseUrl}/api/v1/delegations/${encodeURIComponent(delegationRef)}`, {
410
+ method: "GET",
411
+ headers: {
412
+ "Content-Type": "application/json",
413
+ "User-Agent": "XMCP-I-Verifier/1.0",
414
+ },
415
+ // Add timeout for production reliability
416
+ signal: AbortSignal.timeout(5000),
417
+ });
418
+ if (!response || !response.ok) {
419
+ if (response && response.status === 404) {
420
+ return {
421
+ code: "XMCP_I_EBADPROOF",
422
+ message: "Delegation not found",
423
+ httpStatus: 403,
424
+ details: {
425
+ reason: "Delegation reference not found in KTA",
426
+ remediation: "Check delegation reference and ensure it exists",
427
+ },
428
+ };
429
+ }
430
+ throw new Error(`KTA API error: ${response.status}`);
431
+ }
432
+ const delegation = (await response.json());
433
+ // Cache the response
434
+ this.delegationCache.set(delegationRef, {
435
+ response: delegation,
436
+ expiresAt: Date.now() + this.config.delegationCacheTtl * 1000,
437
+ });
438
+ return this.validateDelegationResponse(delegation, did, keyId);
439
+ }
440
+ catch (error) {
441
+ // Treat delegation check failures as verification failures
442
+ console.warn("Delegation verification failed:", error);
443
+ return {
444
+ code: "XMCP_I_EBADPROOF",
445
+ message: "Delegation verification failed",
446
+ httpStatus: 403,
447
+ details: {
448
+ reason: error instanceof Error ? error.message : "Unknown error",
449
+ remediation: "Check KTA connectivity and delegation status",
450
+ },
451
+ };
452
+ }
453
+ }
454
+ /**
455
+ * Validate delegation response
456
+ */
457
+ validateDelegationResponse(delegation, expectedDid, expectedKeyId) {
458
+ if (!delegation.active) {
459
+ return {
460
+ code: "XMCP_I_EBADPROOF",
461
+ message: "Delegation revoked or inactive",
462
+ httpStatus: 403,
463
+ details: {
464
+ reason: "Delegation is not active",
465
+ remediation: "Renew delegation or use direct identity",
466
+ },
467
+ };
468
+ }
469
+ if (delegation.did !== expectedDid || delegation.keyId !== expectedKeyId) {
470
+ return {
471
+ code: "XMCP_I_EBADPROOF",
472
+ message: "Delegation identity mismatch",
473
+ httpStatus: 403,
474
+ details: {
475
+ reason: "Delegation does not match proof identity",
476
+ expected: `${expectedDid}#${expectedKeyId}`,
477
+ received: `${delegation.did}#${delegation.keyId}`,
478
+ },
479
+ };
480
+ }
481
+ if (delegation.expiresAt && delegation.expiresAt < Date.now() / 1000) {
482
+ return {
483
+ code: "XMCP_I_EBADPROOF",
484
+ message: "Delegation expired",
485
+ httpStatus: 403,
486
+ details: {
487
+ reason: "Delegation has expired",
488
+ remediation: "Renew delegation",
489
+ },
490
+ };
491
+ }
492
+ return null;
493
+ }
494
+ /**
495
+ * Fetch public key from DID document with caching
496
+ */
497
+ async fetchPublicKeyWithCache(did, keyId) {
498
+ const cacheKey = `${did}#${keyId}`;
499
+ const cached = this.didCache.get(cacheKey);
500
+ if (cached && cached.expiresAt > Date.now()) {
501
+ return this.extractPublicKey(cached.document, keyId);
502
+ }
503
+ const didDoc = await this.fetchDIDDocument(did);
504
+ if (!didDoc) {
505
+ return null;
506
+ }
507
+ // Cache the DID document
508
+ this.didCache.set(cacheKey, {
509
+ document: didDoc,
510
+ expiresAt: Date.now() + this.config.didCacheTtl * 1000,
511
+ });
512
+ return this.extractPublicKey(didDoc, keyId);
513
+ }
514
+ /**
515
+ * Fetch DID document from well-known endpoint
516
+ */
517
+ async fetchDIDDocument(did) {
518
+ try {
519
+ if (this.config.allowMockData && did.startsWith("did:test:")) {
520
+ // Mock DID document for testing
521
+ return {
522
+ id: did,
523
+ verificationMethod: [
524
+ {
525
+ id: `#key-test-1`,
526
+ type: "Ed25519VerificationKey2020",
527
+ controller: did,
528
+ publicKeyJwk: {
529
+ kty: "OKP",
530
+ crv: "Ed25519",
531
+ x: "mock-public-key-data",
532
+ },
533
+ },
534
+ ],
535
+ };
536
+ }
537
+ // Convert DID to well-known URL for did:web
538
+ if (!did.startsWith("did:web:")) {
539
+ throw new Error("Only did:web is supported");
540
+ }
541
+ const domain = did.replace("did:web:", "").replace(/:/g, "/");
542
+ const didDocUrl = `https://${domain}/.well-known/did.json`;
543
+ const response = await fetch(didDocUrl, {
544
+ headers: {
545
+ Accept: "application/did+json, application/json",
546
+ "User-Agent": "XMCP-I-Verifier/1.0",
547
+ },
548
+ // Add timeout for production reliability
549
+ signal: AbortSignal.timeout(5000),
550
+ });
551
+ if (!response.ok) {
552
+ throw new Error(`Failed to fetch DID document: ${response.status}`);
553
+ }
554
+ return await response.json();
555
+ }
556
+ catch (error) {
557
+ console.warn("Failed to fetch DID document:", error);
558
+ return null;
559
+ }
560
+ }
561
+ /**
562
+ * Extract public key from DID document
563
+ */
564
+ extractPublicKey(didDoc, keyId) {
565
+ try {
566
+ // Find verification method
567
+ const verificationMethod = didDoc.verificationMethod?.find((vm) => vm.id === `#${keyId}` || vm.id === `${didDoc.id}#${keyId}`);
568
+ if (!verificationMethod) {
569
+ throw new Error(`Key ${keyId} not found in DID document`);
570
+ }
571
+ // Convert to JWK format if needed
572
+ if (verificationMethod.publicKeyJwk) {
573
+ return verificationMethod.publicKeyJwk;
574
+ }
575
+ if (verificationMethod.publicKeyMultibase) {
576
+ // Convert multibase to JWK (simplified for Ed25519)
577
+ // This is a placeholder - real implementation would use proper multibase decoding
578
+ return {
579
+ kty: "OKP",
580
+ crv: "Ed25519",
581
+ x: verificationMethod.publicKeyMultibase.slice(1), // Remove multibase prefix
582
+ };
583
+ }
584
+ throw new Error("Unsupported public key format");
585
+ }
586
+ catch (error) {
587
+ console.warn("Failed to extract public key:", error);
588
+ return null;
589
+ }
590
+ }
591
+ /**
592
+ * Generate trusted headers for successful verification
593
+ */
594
+ generateHeaders(meta) {
595
+ const headers = {
596
+ [AGENT_HEADERS.DID]: meta.did,
597
+ [AGENT_HEADERS.KEY_ID]: meta.kid,
598
+ [AGENT_HEADERS.SESSION]: meta.sessionId,
599
+ [AGENT_HEADERS.CONFIDENCE]: "verified",
600
+ [AGENT_HEADERS.VERIFIED_AT]: Math.floor(Date.now() / 1000).toString(),
601
+ };
602
+ if (meta.scopeId) {
603
+ headers[AGENT_HEADERS.SCOPES] = meta.scopeId;
604
+ }
605
+ if (meta.delegationRef) {
606
+ headers[AGENT_HEADERS.DELEGATION_REF] = meta.delegationRef;
607
+ }
608
+ // Add registry URL for traceability
609
+ headers[AGENT_HEADERS.REGISTRY] = `${this.config.ktaBaseUrl}/agents/${encodeURIComponent(meta.did)}`;
610
+ return headers;
611
+ }
612
+ /**
613
+ * Generate agent context for MCP recipients
614
+ */
615
+ generateAgentContext(meta) {
616
+ return {
617
+ did: meta.did,
618
+ keyId: meta.kid,
619
+ subject: meta.delegationRef ? meta.did : undefined,
620
+ scopes: meta.scopeId ? meta.scopeId.split(",") : [],
621
+ session: meta.sessionId,
622
+ confidence: "verified",
623
+ delegationRef: meta.delegationRef,
624
+ registry: `${this.config.ktaBaseUrl}/agents/${encodeURIComponent(meta.did)}`,
625
+ verifiedAt: Math.floor(Date.now() / 1000),
626
+ };
627
+ }
628
+ /**
629
+ * Create error result from structured error
630
+ */
631
+ createErrorResult(error) {
632
+ return {
633
+ success: false,
634
+ error: {
635
+ code: error.code,
636
+ message: error.message,
637
+ details: error.details,
638
+ httpStatus: error.httpStatus ||
639
+ ERROR_HTTP_STATUS[error.code] ||
640
+ 500,
641
+ },
642
+ };
643
+ }
644
+ /**
645
+ * Log verification attempt for security monitoring
646
+ */
647
+ logVerificationAttempt(context, success, reason, duration) {
648
+ // In production, this would integrate with your logging system
649
+ const logEntry = {
650
+ timestamp: new Date().toISOString(),
651
+ did: context.proof?.meta?.did || "unknown",
652
+ audience: context.audience,
653
+ success,
654
+ reason,
655
+ duration,
656
+ sessionId: context.proof?.meta?.sessionId,
657
+ };
658
+ // For now, just console.log - in production, use structured logging
659
+ console.log("XMCP-I Verification:", JSON.stringify(logEntry));
660
+ }
661
+ /**
662
+ * Clean up expired cache entries
663
+ */
664
+ cleanupCache() {
665
+ const now = Date.now();
666
+ for (const [key, value] of this.didCache.entries()) {
667
+ if (value.expiresAt <= now) {
668
+ this.didCache.delete(key);
669
+ }
670
+ }
671
+ for (const [key, value] of this.delegationCache.entries()) {
672
+ if (value.expiresAt <= now) {
673
+ this.delegationCache.delete(key);
674
+ }
675
+ }
676
+ }
677
+ }
678
+ //# sourceMappingURL=core.js.map