UNPKG

@kya-os/mcp-i

Version:

The TypeScript MCP framework with identity features built-in

262 lines (261 loc) 9.09 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ReceiptVerifier = exports.MerkleLogVerifier = void 0; exports.createReceiptVerifier = createReceiptVerifier; const crypto_1 = require("crypto"); /** * Merkle log verifier implementation */ class MerkleLogVerifier { /** * Verify a receipt's inclusion proof against a log root */ static async verifyInclusion(receipt, logRoot) { try { // Validate receipt format if (!receipt.inclusionProof || !Array.isArray(receipt.inclusionProof)) { return { valid: false, error: "Invalid inclusion proof format", }; } if (receipt.logIndex < 0 || receipt.logIndex >= logRoot.size) { return { valid: false, error: `Log index ${receipt.logIndex} out of bounds for log size ${logRoot.size}`, }; } // Compute leaf hash from receipt content const leafHash = await this.computeLeafHash(receipt); // Verify inclusion proof const computedRoot = await this.computeRootFromProof(leafHash, receipt.logIndex, receipt.inclusionProof); const isValid = computedRoot === logRoot.root; return { valid: isValid, error: isValid ? undefined : "Inclusion proof verification failed", logRoot, }; } catch (error) { return { valid: false, error: error instanceof Error ? error.message : "Verification failed", }; } } /** * Compute leaf hash from receipt content */ static async computeLeafHash(receipt) { // Create canonical representation of receipt for hashing const canonical = { ref: receipt.ref, contentHash: receipt.contentHash, action: receipt.action, ts: receipt.ts, }; const encoder = new TextEncoder(); const data = encoder.encode(JSON.stringify(canonical)); const hashBuffer = await crypto_1.webcrypto.subtle.digest("SHA-256", data); const hashArray = new Uint8Array(hashBuffer); return Array.from(hashArray) .map((b) => b.toString(16).padStart(2, "0")) .join(""); } /** * Compute root hash from inclusion proof */ static async computeRootFromProof(leafHash, leafIndex, proof) { let currentHash = leafHash; let currentIndex = leafIndex; for (const proofHash of proof) { // Determine if current node is left or right child const isLeftChild = currentIndex % 2 === 0; if (isLeftChild) { // Current node is left child, proof hash is right sibling currentHash = await this.hashPair(currentHash, proofHash); } else { // Current node is right child, proof hash is left sibling currentHash = await this.hashPair(proofHash, currentHash); } // Move up to parent level currentIndex = Math.floor(currentIndex / 2); } return currentHash; } /** * Hash a pair of nodes */ static async hashPair(left, right) { const encoder = new TextEncoder(); const data = encoder.encode(left + right); const hashBuffer = await crypto_1.webcrypto.subtle.digest("SHA-256", data); const hashArray = new Uint8Array(hashBuffer); return Array.from(hashArray) .map((b) => b.toString(16).padStart(2, "0")) .join(""); } /** * Verify consistency between two log roots */ static async verifyConsistency(oldRoot, newRoot, consistencyProof) { try { if (oldRoot.size > newRoot.size) { return { valid: false, error: "Old log size cannot be greater than new log size", }; } if (oldRoot.size === newRoot.size) { // Same size, roots should be identical return { valid: oldRoot.root === newRoot.root, error: oldRoot.root === newRoot.root ? undefined : "Roots don't match for same size logs", }; } // TODO: Implement full consistency proof verification // This is a simplified version for demonstration const isValid = consistencyProof.length > 0 && oldRoot.timestamp <= newRoot.timestamp; return { valid: isValid, error: isValid ? undefined : "Consistency proof verification failed", logRoot: newRoot, }; } catch (error) { return { valid: false, error: error instanceof Error ? error.message : "Consistency verification failed", }; } } } exports.MerkleLogVerifier = MerkleLogVerifier; /** * Receipt verifier with policy controls */ class ReceiptVerifier { options; constructor(options = {}) { this.options = options; } /** * Verify a receipt against KTA log */ async verify(receipt) { try { // Skip verification if disabled if (!this.options.enabled) { return { valid: true, error: "Receipt verification disabled", }; } // Fetch current log root from KTA const logRoot = await this.fetchLogRoot(); if (!logRoot) { return { valid: false, error: "Failed to fetch log root from KTA", }; } // Verify inclusion proof return await MerkleLogVerifier.verifyInclusion(receipt, logRoot); } catch (error) { return { valid: false, error: error instanceof Error ? error.message : "Receipt verification failed", }; } } /** * Batch verify multiple receipts */ async verifyBatch(receipts) { const results = []; // Fetch log root once for all receipts const logRoot = this.options.enabled ? await this.fetchLogRoot() : null; for (const receipt of receipts) { if (!this.options.enabled) { results.push({ valid: true, error: "Receipt verification disabled", }); continue; } if (!logRoot) { results.push({ valid: false, error: "Failed to fetch log root from KTA", }); continue; } try { const result = await MerkleLogVerifier.verifyInclusion(receipt, logRoot); results.push(result); } catch (error) { results.push({ valid: false, error: error instanceof Error ? error.message : "Receipt verification failed", }); } } return results; } /** * Fetch current log root from KTA */ async fetchLogRoot() { try { // TODO: Replace with actual KTA API call // This is a placeholder implementation const ktaUrl = this.options.ktaBaseUrl || "https://knowthat.ai"; const response = await fetch(`${ktaUrl}/api/log/root`); if (!response.ok) { throw new Error(`KTA API error: ${response.status}`); } const data = await response.json(); return { root: data.root, size: data.size, timestamp: data.timestamp, }; } catch (error) { // In case of network error, return mock data for testing if (this.options.allowMockData) { return { root: Array.from({ length: 64 }, () => Math.floor(Math.random() * 16).toString(16)).join(""), size: 10000, timestamp: Math.floor(Date.now() / 1000), }; } console.warn("Failed to fetch log root from KTA:", error); return null; } } } exports.ReceiptVerifier = ReceiptVerifier; /** * Create receipt verifier instance */ function createReceiptVerifier(options) { return new ReceiptVerifier({ enabled: true, ktaBaseUrl: "https://knowthat.ai", allowMockData: false, cacheLogRootTtl: 300, // 5 minutes ...options, }); }