UNPKG

@synet/keys

Version:

Zero-dependency, secure key generation library. Supports ed25519, x25519, secp256k1, RSA, and WireGuard keys.

446 lines (440 loc) 15.9 kB
"use strict"; /** * Signer Unit - Primary cryptographic signing unit * [🔐] Self-contained cryptographic engine that knows how to sign * * Design principles: * - Signer is the primary unit (holds private key securely) * - Full Unit architecture with execute, teach, capabilities * - Self-contained cryptographic operations (no external dependencies) * - Can teach capabilities to Key units * * @author Synet Team */ 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.Signer = void 0; const unit_1 = require("@synet/unit"); const keys_1 = require("./keys"); const verify_1 = require("./verify"); const utils_1 = require("./utils"); const crypto = __importStar(require("node:crypto")); const key_1 = require("./key"); /** * Signer Unit - Primary unit for key generation and signing * The source of truth for cryptographic operations */ class Signer extends unit_1.Unit { constructor(props) { super(props); // Register capabilities this._addCapability('sign', (...args) => { // Handle both direct string and object with data property const data = typeof args[0] === 'string' ? args[0] : args[0]?.data; return this.sign(data); }); this._addCapability('getPublicKey', () => this.getPublicKey()); this._addCapability('getPublicKeyHex', () => this.getPublicKeyHex()); this._addCapability('getPrivateKeyHex', () => this.getPrivateKeyHex()); this._addCapability('verify', (...args) => { // Handle both direct strings and object with data/signature properties if (typeof args[0] === 'string' && typeof args[1] === 'string') { return this.verify(args[0], args[1]); } const obj = args[0]; return this.verify(obj.data, obj.signature); }); this._addCapability('getAlgorithm', () => this.getAlgorithm()); this._addCapability('getKey', (...args) => this.getKey(args[0])); this._addCapability('toJSON', () => this.toJSON()); } /** * Generate new signer with fresh key pair */ static generate(keyType, params = {}) { try { const keyPair = (0, keys_1.generateKeyPair)(keyType); if (!keyPair || !keyPair.privateKey || !keyPair.publicKey) { throw new Error('Failed to generate key pair'); } const props = { dna: (0, unit_1.createUnitSchema)({ id: 'signer', version: '1.0.0' }), privateKeyPEM: keyPair.privateKey, publicKeyPEM: keyPair.publicKey, keyType, keyId: (0, utils_1.createId)(), metadata: params?.metadata || {}, created: new Date(), secure: params?.secure !== undefined ? params.secure : false, }; console.log(`[🔐] Generated new Signer with key type: ${keyType}, secure: ${props.secure} metadata: ${JSON.stringify(props.metadata)}`); return new Signer(props); } catch (error) { console.error('[🔐] Failed to generate signer:', error); throw new Error('Failed to generate key pair'); } } /** * Create signer from existing key pair */ static create(config) { try { if (!config.privateKeyPEM || !config.publicKeyPEM || !config.keyType) { throw new Error('Invalid parameters, privateKeyPEM, publicKeyPEM, and keyType are required'); } const props = { dna: (0, unit_1.createUnitSchema)({ id: 'signer', version: '1.0.0' }), privateKeyPEM: config.privateKeyPEM, publicKeyPEM: config.publicKeyPEM, keyType: config.keyType, keyId: (0, utils_1.createId)(), metadata: config.metadata || {}, created: new Date(), secure: config.secure || true, // Use secure signing methods if specified }; return new Signer(props); } catch (error) { console.error('[🔐] Failed to create signer:', error); throw error; } } /** * Create signer from existing key pair (compatibility method) */ static createFromKeyPair(privateKeyPEM, publicKeyPEM, keyType, metadata) { return Signer.create({ privateKeyPEM, publicKeyPEM, keyType, metadata }); } /** * Create signer with external ISigner (edge case) * For cases where signing logic is handled externally */ static createWithSigner(params) { try { // Create a Signer with external signing capability // Note: Public key should be provided in metadata if needed const publicKeyPEM = params.publicKeyPEM || ''; const keyType = params.keyType || 'ed25519'; const props = { dna: (0, unit_1.createUnitSchema)({ id: 'signer', version: '1.0.0' }), privateKeyPEM: '', // External signer handles signing publicKeyPEM, keyType, keyId: (0, utils_1.createId)(), metadata: params.metadata || {}, isigner: params.signer, created: new Date(), secure: true, }; return new Signer(props); } catch (error) { console.error('[🔐] Failed to create signer with external ISigner:', error); return null; } } // Unit implementation whoami() { return `[🔐] Signer Unit - Secure ${this.props.keyType} cryptographic engine (${this.props.keyId.slice(0, 8)})`; } capabilities() { return this._getAllCapabilities(); } help() { console.log(` [🔐] Signer Unit - Self-Contained Cryptographic Engine Identity: ${this.whoami()} Algorithm: ${this.props.keyType} Core Capabilities: - sign(data): Sign data with private key - getPublicKey(): Get public key for sharing - getPublicKeyHex(): Get public key in hex format - getPrivateKeyHex(): Get private key in hex format (secure mode dependent) - verify(data, signature): Verify signatures - getAlgorithm(): Get signing algorithm - getKey(): Get data needed to create associated Key unit - toJSON(): Export metadata (no private key) Unit Operations: - execute(capability, ...args): Execute any capability - teach(): Share all capabilities with other units - capabilities(): List all available capabilities - learn(capabilities): Absorb capabilities from other units Security Contract: - Private key never exposed outside unit - Implements ISigner interface for external compatibility - Can be used directly or through learned capabilities Try me: const signer = Signer.generate('ed25519', { name: 'my-signer' }); await signer.execute('sign', 'hello world'); const keyData = await signer.execute('getKey', { name: 'my-key' }); // Then use Key.createFromSigner(signer, keyData.meta) separately const publicKey = await signer.execute('getPublicKey'); `); } teach() { return { unitId: this.dna.id, capabilities: { sign: (...args) => this.sign(args[0]), getPublicKey: () => this.getPublicKey(), getPublicKeyHex: () => this.getPublicKeyHex(), getPrivateKeyHex: () => this.getPrivateKeyHex(), verify: (...args) => this.verify(args[0], args[1]), getAlgorithm: () => this.getAlgorithm(), toJSON: () => this.toJSON(), getKeyType: () => this.props.keyType, } }; } // ISigner implementation async sign(data) { try { // Use external signer if available, otherwise use native signing if (this.props.isigner) { return this.props.isigner.sign(data); } return this.performSigning(data, this.props.privateKeyPEM, this.props.keyType); } catch (error) { throw new Error(`[🔐] Signing failed: ${error}`); } } getPublicKey() { return this.props.publicKeyPEM; } getAlgorithm() { return this.props.keyType; } /** * Create a Key unit from this Signer's key material * The Key will learn signing capabilities from this Signer */ createKey() { try { const key = key_1.Key.create({ publicKeyPEM: this.props.publicKeyPEM, keyType: this.props.keyType, meta: { ...this.props.metadata } }); if (key) { // Teach the key our signing capabilities const teaching = this.teach(); key.learn([teaching]); } return key; } catch (error) { console.error('[🔐] Failed to create key:', error); return null; } } // Additional capabilities async verify(data, signature) { try { return this.performVerification(data, signature, this.props.publicKeyPEM, this.props.keyType); } catch { return false; } } /** * Get data needed to create associated Key unit * Returns the data needed for Key.createFromSigner() to avoid circular dependency */ getKey(meta) { return { publicKeyPEM: this.props.publicKeyPEM, keyType: this.props.keyType, meta: { ...meta }, signer: this }; } /** * Export signer metadata (excludes private key for security) */ toJSON() { return { id: this.props.keyId, publicKeyPEM: this.props.publicKeyPEM, type: this.props.keyType, meta: this.props.meta, canSign: true, algorithm: this.props.keyType }; } // Getters for internal access get id() { return this.props.keyId; } get type() { return this.props.keyType; } get metadata() { return { ...this.props.metadata }; } get privateKeyPEM() { return this.props.secure ? '' : this.props.privateKeyPEM; } get publicKeyPEM() { return this.props.publicKeyPEM; } get keyType() { return this.props.keyType; } /** * Convert key to PEM format for cryptographic operations */ convertToPEMFormat(keyData, keyType, isPublic = false) { // Our keys are generated in PEM format by default, so they should already be PEM if (keyData.includes('-----BEGIN')) { return keyData; } // If we have a hex format key, we need to convert it throw new Error(`Key format conversion needed for ${keyType} key. Expected PEM format but got hex.`); } /** * Perform cryptographic signing based on key type */ performSigning(data, privateKey, keyType) { if (!data || !privateKey) { throw new Error('Invalid input: data and privateKey are required'); } try { switch (keyType) { case 'ed25519': return this.signEd25519(data, privateKey); case 'rsa': return this.signRSA(data, privateKey); case 'secp256k1': return this.signSecp256k1(data, privateKey); case 'x25519': throw new Error('X25519 is for key exchange, not signing'); case 'wireguard': throw new Error('WireGuard keys are for VPN, not signing'); default: throw new Error(`Unsupported key type for signing: ${keyType}`); } } catch (error) { throw new Error(`Signing failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * Perform cryptographic verification based on key type * Uses tested verification functions from verify.ts */ performVerification(data, signature, publicKey, keyType) { return (0, verify_1.verifySignature)(data, signature, publicKey, keyType); } /** * Sign data with Ed25519 key */ signEd25519(data, privateKey) { const signature = crypto.sign(null, Buffer.from(data), { key: privateKey, format: 'pem', }); const base64Signature = signature.toString('base64'); return (0, utils_1.base64ToBase64Url)(base64Signature); } /** * Sign data with RSA key */ signRSA(data, privateKey) { const sign = crypto.createSign('SHA256'); sign.update(data); sign.end(); const base64Signature = sign.sign(privateKey, 'base64'); return (0, utils_1.base64ToBase64Url)(base64Signature); } /** * Sign data with secp256k1 key */ signSecp256k1(data, privateKey) { // For secp256k1, we use ECDSA with SHA256 const sign = crypto.createSign('SHA256'); sign.update(data); sign.end(); const base64Signature = sign.sign(privateKey, 'base64'); return (0, utils_1.base64ToBase64Url)(base64Signature); } /** * Get public key in hex format for DID generation * This allows DID generation to work immediately with hex format */ getPublicKeyHex() { try { // Use the pemToHex utility from keys.ts return (0, keys_1.pemToHex)(this.props.publicKeyPEM); } catch (error) { console.error('[🔐] Failed to convert public key to hex:', error); return null; } } /** * Get private key in hex format (respects security flag) * Only returns private key if secure flag is false */ getPrivateKeyHex() { if (this.props.secure) { console.warn('Private key access denied - secure mode enabled'); return null; } try { return (0, keys_1.pemPrivateKeyToHex)(this.props.privateKeyPEM); } catch (error) { console.error('Failed to convert private key to hex:', error); return null; } } } exports.Signer = Signer;