UNPKG

@fedmcp/core

Version:

Federal Model Context Protocol - TypeScript implementation

99 lines 3.61 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Verifier = void 0; const crypto_1 = require("crypto"); const signer_1 = require("./signer"); class Verifier { constructor() { this.publicKeys = new Map(); } async addPublicKey(keyId, publicKey) { this.publicKeys.set(keyId, publicKey); } async addPublicKeyJWK(jwk) { const keyId = jwk.kid || await this.generateKeyIdFromJWK(jwk); const publicKey = await crypto_1.subtle.importKey('jwk', jwk, { name: 'ECDSA', namedCurve: 'P-256' }, true, ['verify']); this.publicKeys.set(keyId, publicKey); } async verify(jws) { // Split JWS into parts const parts = jws.split('.'); if (parts.length !== 3) { throw new Error('Invalid JWS format'); } const [encodedHeader, encodedPayload, encodedSignature] = parts; // Decode header const headerJson = new TextDecoder().decode((0, signer_1.base64urlDecode)(encodedHeader)); const header = JSON.parse(headerJson); const keyId = header.kid; if (!keyId) { throw new Error('No key ID in JWS header'); } const publicKey = this.publicKeys.get(keyId); if (!publicKey) { throw new Error(`Unknown key ID: ${keyId}`); } // Verify signature const signingInput = `${encodedHeader}.${encodedPayload}`; const signature = (0, signer_1.base64urlDecode)(encodedSignature); const isValid = await crypto_1.subtle.verify({ name: 'ECDSA', hash: 'SHA-256' }, publicKey, signature, new TextEncoder().encode(signingInput)); if (!isValid) { throw new Error('Invalid signature'); } // Decode and validate payload const payloadJson = new TextDecoder().decode((0, signer_1.base64urlDecode)(encodedPayload)); const payload = JSON.parse(payloadJson); // Extract artifact if (!payload.artifact) { throw new Error('No artifact in JWS payload'); } const artifact = payload.artifact; // Verify claims if (artifact.id !== payload.sub) { throw new Error("Artifact ID doesn't match subject claim"); } if (artifact.workspaceId !== payload.iss) { throw new Error("Workspace ID doesn't match issuer claim"); } // Check expiration const now = Math.floor(Date.now() / 1000); if (payload.exp && payload.exp < now) { throw new Error('Token has expired'); } return artifact; } async verifyWithResult(jws) { try { const artifact = await this.verify(jws); return { valid: true, claims: { artifact } }; } catch (error) { return { valid: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async generateKeyIdFromJWK(jwk) { // Generate key ID from JWK coordinates const keyMaterial = `${jwk.x || ''}${jwk.y || ''}`; const encoder = new TextEncoder(); const data = encoder.encode(keyMaterial); const hash = await crypto_1.subtle.digest('SHA-256', data); return Array.from(new Uint8Array(hash)) .slice(0, 8) .map(b => b.toString(16).padStart(2, '0')) .join(''); } } exports.Verifier = Verifier; //# sourceMappingURL=verifier.js.map