@fedmcp/core
Version:
Federal Model Context Protocol - TypeScript implementation
129 lines • 4.22 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.LocalSigner = exports.Signer = void 0;
exports.base64urlDecode = base64urlDecode;
const crypto_1 = require("crypto");
const artifact_1 = require("./artifact");
class Signer {
}
exports.Signer = Signer;
class LocalSigner extends Signer {
constructor(privateKey) {
super();
this.privateKey = null;
this.publicKey = null;
if (privateKey) {
this.privateKey = privateKey;
}
this.keyId = '';
}
async initialize() {
if (!this.privateKey) {
// Generate a new P-256 key pair
const keyPair = await crypto_1.subtle.generateKey({
name: 'ECDSA',
namedCurve: 'P-256'
}, true, ['sign', 'verify']);
this.privateKey = keyPair.privateKey;
this.publicKey = keyPair.publicKey;
}
else {
// Extract public key from private key (if needed)
// Note: Web Crypto API doesn't directly support this, would need to export/import
}
// Generate key ID from public key
if (this.publicKey) {
const publicKeyData = await crypto_1.subtle.exportKey('spki', this.publicKey);
const hash = await crypto_1.subtle.digest('SHA-256', publicKeyData);
this.keyId = Array.from(new Uint8Array(hash))
.slice(0, 8)
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
}
async sign(artifact) {
if (!this.privateKey) {
await this.initialize();
}
// Validate artifact
const fedArtifact = new artifact_1.FedMCPArtifact(artifact);
fedArtifact.validate();
// Create JWS header
const header = {
alg: 'ES256',
typ: 'JWT',
kid: this.keyId
};
// Create JWT payload
const now = Math.floor(Date.now() / 1000);
const payload = {
iss: artifact.workspaceId,
sub: artifact.id,
iat: now,
exp: now + (90 * 24 * 60 * 60), // 90 days
artifact: artifact
};
// Encode header and payload
const encodedHeader = base64url(JSON.stringify(header));
const encodedPayload = base64url(JSON.stringify(payload));
// Create signing input
const signingInput = `${encodedHeader}.${encodedPayload}`;
// Sign with ECDSA
const signature = await crypto_1.subtle.sign({
name: 'ECDSA',
hash: 'SHA-256'
}, this.privateKey, new TextEncoder().encode(signingInput));
// Convert signature to base64url
const encodedSignature = base64url(new Uint8Array(signature));
// Return complete JWS
return `${signingInput}.${encodedSignature}`;
}
getKeyId() {
return this.keyId;
}
async getPublicKeyJWK() {
if (!this.publicKey) {
await this.initialize();
}
const jwk = await crypto_1.subtle.exportKey('jwk', this.publicKey);
return {
...jwk,
use: 'sig',
kid: this.keyId
};
}
}
exports.LocalSigner = LocalSigner;
// Helper function to encode to base64url
function base64url(input) {
let base64;
if (typeof input === 'string') {
base64 = btoa(input);
}
else {
// Convert Uint8Array to string
const binary = String.fromCharCode(...input);
base64 = btoa(binary);
}
return base64
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
// Helper function to decode from base64url
function base64urlDecode(input) {
// Add padding if necessary
const padded = input + '=='.slice(0, (4 - input.length % 4) % 4);
// Convert base64url to base64
const base64 = padded
.replace(/-/g, '+')
.replace(/_/g, '/');
// Decode
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes;
}
//# sourceMappingURL=signer.js.map