@kya-os/mcp-i
Version:
The TypeScript MCP framework with identity features built-in
152 lines (151 loc) • 5.76 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SimpleEncryption = exports.AudienceKeyEncryption = void 0;
const crypto_1 = require("crypto");
/**
* Audience-key encryption for ktaEncrypted mode
* Uses X25519 key exchange + ChaCha20-Poly1305 AEAD
*/
class AudienceKeyEncryption {
/**
* Generate a new key pair for encryption
*/
static async generateKeyPair() {
const keyPair = await crypto_1.webcrypto.subtle.generateKey({
name: "X25519",
}, true, // extractable
["deriveKey"]);
// Type assertion since we know X25519 returns a CryptoKeyPair
return keyPair;
}
/**
* Encrypt data for a specific audience public key
*/
static async encrypt(data, audiencePublicKey) {
// Generate ephemeral key pair
const ephemeralKeyPair = await this.generateKeyPair();
// Import audience public key
const audiencePubKey = await this.importPublicKey(audiencePublicKey);
// Derive shared secret
const sharedSecret = await crypto_1.webcrypto.subtle.deriveKey({
name: "X25519",
public: audiencePubKey,
}, ephemeralKeyPair.privateKey, {
name: "ChaCha20-Poly1305",
length: 256,
}, false, // not extractable
["encrypt"]);
// Generate random nonce
const nonce = crypto_1.webcrypto.getRandomValues(new Uint8Array(12));
// Encrypt the data
const encoder = new TextEncoder();
const dataBytes = encoder.encode(data);
const ciphertext = await crypto_1.webcrypto.subtle.encrypt({
name: "ChaCha20-Poly1305",
iv: nonce,
}, sharedSecret, dataBytes);
// Export ephemeral public key
const ephemeralPublicKey = await this.exportPublicKey(ephemeralKeyPair.publicKey);
return {
ciphertext: Buffer.from(ciphertext).toString("base64"),
nonce: Buffer.from(nonce).toString("base64"),
publicKey: ephemeralPublicKey,
};
}
/**
* Decrypt data using private key
*/
static async decrypt(params) {
// Import keys
const ephemeralPublicKey = await this.importPublicKey(params.publicKey);
const privateKey = await this.importPrivateKey(params.privateKey);
// Derive shared secret
const sharedSecret = await crypto_1.webcrypto.subtle.deriveKey({
name: "X25519",
public: ephemeralPublicKey,
}, privateKey, {
name: "ChaCha20-Poly1305",
length: 256,
}, false, // not extractable
["decrypt"]);
// Decrypt the data
const ciphertext = Buffer.from(params.ciphertext, "base64");
const nonce = Buffer.from(params.nonce, "base64");
const decrypted = await crypto_1.webcrypto.subtle.decrypt({
name: "ChaCha20-Poly1305",
iv: nonce,
}, sharedSecret, ciphertext);
const decoder = new TextDecoder();
return decoder.decode(decrypted);
}
/**
* Import public key from base64 string
*/
static async importPublicKey(publicKey) {
const keyBytes = Buffer.from(publicKey, "base64");
return await crypto_1.webcrypto.subtle.importKey("raw", keyBytes, {
name: "X25519",
}, false, []);
}
/**
* Import private key from base64 string
*/
static async importPrivateKey(privateKey) {
const keyBytes = Buffer.from(privateKey, "base64");
return await crypto_1.webcrypto.subtle.importKey("raw", keyBytes, {
name: "X25519",
}, false, ["deriveKey"]);
}
/**
* Export public key to base64 string
*/
static async exportPublicKey(publicKey) {
const keyBytes = await crypto_1.webcrypto.subtle.exportKey("raw", publicKey);
return Buffer.from(keyBytes).toString("base64");
}
/**
* Export private key to base64 string
*/
static async exportPrivateKey(privateKey) {
const keyBytes = await crypto_1.webcrypto.subtle.exportKey("raw", privateKey);
return Buffer.from(keyBytes).toString("base64");
}
}
exports.AudienceKeyEncryption = AudienceKeyEncryption;
/**
* Simple symmetric encryption for testing
*/
class SimpleEncryption {
/**
* Encrypt data with a password (for testing only)
*/
static async encrypt(data, password) {
// This is a simple implementation for testing
// In production, use proper key derivation and authenticated encryption
const encoder = new TextEncoder();
const dataBytes = encoder.encode(data);
const passwordBytes = encoder.encode(password);
// Simple XOR encryption (NOT secure, for testing only)
const encrypted = new Uint8Array(dataBytes.length);
for (let i = 0; i < dataBytes.length; i++) {
encrypted[i] = dataBytes[i] ^ passwordBytes[i % passwordBytes.length];
}
return Buffer.from(encrypted).toString("base64");
}
/**
* Decrypt data with a password (for testing only)
*/
static async decrypt(encryptedData, password) {
const encrypted = Buffer.from(encryptedData, "base64");
const encoder = new TextEncoder();
const passwordBytes = encoder.encode(password);
// Simple XOR decryption (NOT secure, for testing only)
const decrypted = new Uint8Array(encrypted.length);
for (let i = 0; i < encrypted.length; i++) {
decrypted[i] = encrypted[i] ^ passwordBytes[i % passwordBytes.length];
}
const decoder = new TextDecoder();
return decoder.decode(decrypted);
}
}
exports.SimpleEncryption = SimpleEncryption;