@kya-os/mcp-i
Version:
COMING SOON:Production-ready MCP Identity with automatic registration, key rotation, and optimized performance
213 lines (212 loc) • 8.16 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateKeyPair = generateKeyPair;
exports.sign = sign;
exports.verify = verify;
exports.generateNonce = generateNonce;
exports.generateNonceSync = generateNonceSync;
exports.constantTimeEqual = constantTimeEqual;
exports.publicKeyToDid = publicKeyToDid;
exports.encrypt = encrypt;
exports.decrypt = decrypt;
exports.clearCache = clearCache;
let ed25519 = null;
let cryptoModule = null;
const signatureCache = new Map();
const MAX_CACHE_SIZE = 100;
async function loadEd25519() {
if (!ed25519) {
ed25519 = await Promise.resolve().then(() => __importStar(require('@noble/ed25519')));
}
return ed25519;
}
async function loadCrypto() {
if (!cryptoModule) {
cryptoModule = await Promise.resolve().then(() => __importStar(require('crypto')));
}
return cryptoModule;
}
async function generateKeyPair() {
const ed = await loadEd25519();
const privateKeyBytes = ed.utils.randomPrivateKey();
const publicKeyBytes = await ed.getPublicKeyAsync(privateKeyBytes);
const publicKey = Buffer.from(publicKeyBytes).toString('base64');
const privateKey = Buffer.from(privateKeyBytes).toString('base64');
return {
publicKey,
privateKey,
publicKeyBytes,
privateKeyBytes
};
}
async function sign(message, privateKeyBase64) {
const messageStr = typeof message === 'string' ? message : message.toString('base64');
const cacheKey = `${privateKeyBase64}:${messageStr}`;
const cached = signatureCache.get(cacheKey);
if (cached) {
return cached;
}
const ed = await loadEd25519();
const messageBuffer = typeof message === 'string'
? Buffer.from(message, 'utf-8')
: message;
const privateKey = Buffer.from(privateKeyBase64, 'base64');
const signature = await ed.signAsync(messageBuffer, privateKey);
const signatureBase64 = Buffer.from(signature).toString('base64');
if (signatureCache.size >= MAX_CACHE_SIZE) {
const firstKey = signatureCache.keys().next().value;
if (firstKey) {
signatureCache.delete(firstKey);
}
}
signatureCache.set(cacheKey, signatureBase64);
return signatureBase64;
}
async function verify(message, signatureBase64, publicKeyBase64) {
try {
const ed = await loadEd25519();
const messageBuffer = typeof message === 'string'
? Buffer.from(message, 'utf-8')
: message;
const signature = Buffer.from(signatureBase64, 'base64');
const publicKey = Buffer.from(publicKeyBase64, 'base64');
return await ed.verifyAsync(signature, messageBuffer, publicKey);
}
catch {
return false;
}
}
async function generateNonce(length = 32) {
const crypto = await loadCrypto();
return crypto.randomBytes(length).toString('hex');
}
let cachedCrypto = null;
function generateNonceSync(length = 32) {
if (typeof globalThis.crypto !== 'undefined' && globalThis.crypto.getRandomValues) {
const bytes = new Uint8Array(length);
globalThis.crypto.getRandomValues(bytes);
return Buffer.from(bytes).toString('hex');
}
else {
if (!cachedCrypto) {
const hex = '0123456789abcdef';
let output = '';
for (let i = 0; i < length * 2; i++) {
output += hex[Math.floor(Math.random() * 16)];
}
console.warn('Using Math.random for nonce generation - not cryptographically secure!');
return output;
}
return cachedCrypto.randomBytes(length).toString('hex');
}
}
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
loadCrypto().then(crypto => {
cachedCrypto = crypto;
}).catch(() => {
});
}
function constantTimeEqual(a, b) {
if (a.length !== b.length) {
return false;
}
let result = 0;
for (let i = 0; i < a.length; i++) {
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
}
return result === 0;
}
function publicKeyToDid(publicKeyBase64) {
const publicKey = Buffer.from(publicKeyBase64, 'base64');
const multicodec = Buffer.from([0xed, 0x01]);
const multikey = Buffer.concat([multicodec, publicKey]);
return `did:key:z${multikey.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')}`;
}
async function encrypt(data, password) {
const encoder = new TextEncoder();
const salt = new Uint8Array(16);
globalThis.crypto.getRandomValues(salt);
const keyMaterial = await globalThis.crypto.subtle.importKey('raw', encoder.encode(password), 'PBKDF2', false, ['deriveKey']);
const key = await globalThis.crypto.subtle.deriveKey({
name: 'PBKDF2',
salt,
iterations: 100000,
hash: 'SHA-256'
}, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['encrypt']);
const iv = new Uint8Array(12);
globalThis.crypto.getRandomValues(iv);
const encrypted = await globalThis.crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encoder.encode(data));
const combined = new Uint8Array(salt.length + iv.length + encrypted.byteLength);
combined.set(salt);
combined.set(iv, salt.length);
combined.set(new Uint8Array(encrypted), salt.length + iv.length);
return 'enc:' + Buffer.from(combined).toString('base64');
}
async function decrypt(encryptedData, password) {
let dataToDecrypt = encryptedData;
if (encryptedData.startsWith('enc:')) {
dataToDecrypt = encryptedData.slice(4);
}
if (!dataToDecrypt || dataToDecrypt.length < 44) {
return encryptedData;
}
const encoder = new TextEncoder();
const decoder = new TextDecoder();
try {
const combined = Buffer.from(dataToDecrypt, 'base64');
if (combined.length < 29) {
return encryptedData;
}
const salt = combined.slice(0, 16);
const iv = combined.slice(16, 28);
const encrypted = combined.slice(28);
const keyMaterial = await globalThis.crypto.subtle.importKey('raw', encoder.encode(password), 'PBKDF2', false, ['deriveKey']);
const key = await globalThis.crypto.subtle.deriveKey({
name: 'PBKDF2',
salt: new Uint8Array(salt),
iterations: 100000,
hash: 'SHA-256'
}, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['decrypt']);
const decrypted = await globalThis.crypto.subtle.decrypt({ name: 'AES-GCM', iv: new Uint8Array(iv) }, key, new Uint8Array(encrypted));
return decoder.decode(decrypted);
}
catch (error) {
throw new Error('Failed to decrypt data: invalid password or corrupted data');
}
}
function clearCache() {
signatureCache.clear();
}