import * as fs from "fs" import * as path from "path" import * as crypto from "crypto" export interface SkillSignature { algorithm: string signature: string publicKey: string signedAt: string skillName: string contentHash: string } const SIGNATURE_FILE = "SIGNATURE.json" export function hashSkillContent(skillDir: string): string { const mdPath = path.join(skillDir, "SKILL.md") if (!fs.existsSync(mdPath)) { throw new Error(`No SKILL.md found in ${skillDir}`) } const content = fs.readFileSync(mdPath, "utf-8") return crypto.createHash("sha256").update(content).digest("hex") } export function signSkill( skillDir: string, privateKey: string, publicKey: string, ): SkillSignature { const contentHash = hashSkillContent(skillDir) const skillName = path.basename(skillDir) const signature = crypto .createSign("sha256") .update(contentHash) .sign(privateKey, "base64") const sig: SkillSignature = { algorithm: "sha256-rsa", signature, publicKey, signedAt: new Date().toISOString(), skillName, contentHash, } const sigPath = path.join(skillDir, SIGNATURE_FILE) fs.writeFileSync(sigPath, JSON.stringify(sig, null, 2), "utf-8") return sig } export function verifySkill( skillDir: string, expectedPublicKey?: string, ): { valid: boolean; reason?: string } { const sigPath = path.join(skillDir, SIGNATURE_FILE) const mdPath = path.join(skillDir, "SKILL.md") if (!fs.existsSync(sigPath)) { return { valid: false, reason: "No signature file found" } } if (!fs.existsSync(mdPath)) { return { valid: false, reason: "No SKILL.md found" } } try { const sig: SkillSignature = JSON.parse(fs.readFileSync(sigPath, "utf-8")) const content = fs.readFileSync(mdPath, "utf-8") const contentHash = crypto.createHash("sha256").update(content).digest("hex") if (contentHash !== sig.contentHash) { return { valid: false, reason: "Content hash mismatch - skill has been modified" } } if (expectedPublicKey && sig.publicKey !== expectedPublicKey) { return { valid: false, reason: "Public key mismatch - signed by different author" } } const verifier = crypto.createVerify("sha256") verifier.update(sig.contentHash) const isValid = verifier.verify(sig.publicKey, sig.signature, "base64") if (!isValid) { return { valid: false, reason: "Signature verification failed" } } return { valid: true } } catch (err) { return { valid: false, reason: `Verification error: ${(err as Error).message}` } } } export function generateKeyPair(): { publicKey: string; privateKey: string } { const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", { modulusLength: 2048, publicKeyEncoding: { type: "spki", format: "pem" }, privateKeyEncoding: { type: "pkcs8", format: "pem" }, }) return { publicKey, privateKey } }