@synet/keys
Version:
Zero-dependency, secure key generation library. Supports ed25519, x25519, secp256k1, RSA, and WireGuard keys.
220 lines (215 loc) • 7.72 kB
JavaScript
"use strict";
/**
* Key Unit - Public-facing cryptographic key unit
* [🔑] Clean unit that holds public key material and can learn signing capabilities
*
* Design principles:
* - Key is the public-facing unit (holds public key only)
* - Can learn signing capabilities from Signer units via teach/learn pattern
* - Simple getters for public properties (no complex methods)
* - Full Unit architecture with execute, learn, teach, capabilities
*
* @author Synet Team
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Key = void 0;
const unit_1 = require("@synet/unit");
const utils_1 = require("./utils");
class Key extends unit_1.Unit {
constructor(props) {
super(props);
// Register capabilities
// Note: 'sign' and 'verify' capabilities are added dynamically when learned from Signer
this._addCapability('getPublicKey', () => this.getPublicKey());
this._addCapability('canSign', () => this.canSign());
this._addCapability('toJSON', () => this.toJSON());
}
// Public property getters (props-based access)
get publicKeyPEM() {
return this.props.publicKeyPEM;
}
get keyType() {
return this.props.keyType;
}
get keyId() {
return this.props.keyId;
}
get meta() {
return { ...this.props.meta };
}
/**
* Create Key unit from config
*/
static create(config) {
try {
if (!config.publicKeyPEM || !config.keyType) {
throw new Error('Invalid parameters, publicKeyPEM and keyType are required');
}
// Validate key type
const validKeyTypes = ['ed25519', 'rsa', 'secp256k1', 'x25519', 'wireguard'];
if (!validKeyTypes.includes(config.keyType)) {
throw new Error(`Invalid key type: ${config.keyType}`);
}
// Validate public key format (basic check)
if (!Key.isValidPublicKey(config.publicKeyPEM, config.keyType)) {
throw new Error(`Invalid public key format for key type: ${config.keyType}`);
}
const props = {
dna: (0, unit_1.createUnitSchema)({
id: 'key-unit',
version: '1.0.0'
}),
publicKeyPEM: config.publicKeyPEM,
keyType: config.keyType,
keyId: (0, utils_1.createId)(),
meta: config.meta || {},
created: new Date()
};
return new Key(props);
}
catch (error) {
console.error('[🔑] Failed to create key:', error);
throw error;
}
}
static isValidPublicKey(publicKeyPEM, keyType) {
try {
// Basic validation - check for PEM format
if (!publicKeyPEM.includes('-----BEGIN') || !publicKeyPEM.includes('-----END')) {
return false;
}
// Key type specific validation
switch (keyType) {
case 'ed25519':
return publicKeyPEM.includes('PUBLIC KEY');
case 'rsa':
return publicKeyPEM.includes('PUBLIC KEY') || publicKeyPEM.includes('RSA PUBLIC KEY');
case 'secp256k1':
case 'x25519':
case 'wireguard':
return publicKeyPEM.includes('PUBLIC KEY');
default:
return false;
}
}
catch {
return false;
}
}
// Unit implementation
whoami() {
return `[🔑] Key Unit - ${this.props.keyType} public key (${this.props.keyId.slice(0, 8)})`;
}
capabilities() {
return this._getAllCapabilities();
}
help() {
console.log(`
[🔑] Key Unit - Clean Public Key Carrier
Identity: ${this.whoami()}
Key Type: ${this.props.keyType}
Key ID: ${this.props.keyId}
Public Properties:
- publicKeyPEM: ${this.props.publicKeyPEM.substring(0, 50)}...
- keyType: ${this.props.keyType}
- keyId: ${this.props.keyId}
Capabilities: ${this.capabilities().join(', ')}
The Key unit holds public key material and can learn signing capabilities
from Signer units through the teach/learn pattern.
`);
}
teach() {
return {
unitId: this.dna.id,
capabilities: {
getPublicKey: () => this.props.publicKeyPEM,
getKeyType: () => this.props.keyType,
getKeyId: () => this.props.keyId,
toJSON: () => this.toJSON(),
sign: (...args) => this.sign(args[0]),
verify: (...args) => this.verify(args[0], args[1]),
}
};
}
/**
* Verify signature using public key
* Must learn verification capability from Signer to use this method
*/
async verify(data, signature) {
try {
// Use learned verification capability (preferred and secure)
if (this.capabilities().includes('verify')) {
return this.execute('verify', data, signature);
}
// No fallback - Keys must learn verification from Signer
throw new Error('[🔑] Cannot verify without learning verification capability from Signer. Use signer.createKey() or key.learn([signerTeaching])');
}
catch (error) {
if (error instanceof Error && error.message.includes('Cannot verify without learning')) {
throw error;
}
return false;
}
}
/**
* Sign data - uses learned signing capability if available
*/
async sign(data) {
if (this.canSign()) {
return this.execute('sign', data);
}
throw new Error('[🔑] Cannot sign with public-only key. Use signer.createKey() or key.learn([signerTeaching]) to enable signing.');
}
/**
* Check if this key can sign
* Returns true if signing capabilities have been learned
*/
canSign() {
return this.capabilities().includes('sign');
}
/**
* Get public key (compatibility method)
*/
getPublicKey() {
return this.props.publicKeyPEM;
}
toJSON() {
return {
unitId: this.dna.id,
keyId: this.props.keyId,
publicKeyPEM: this.props.publicKeyPEM,
keyType: this.props.keyType,
meta: this.props.meta,
created: this.props.created,
capabilities: this.capabilities()
};
}
/**
* Override learn method to handle Signer teachings
*/
async learn(teachings) {
try {
for (const teaching of teachings) {
if (teaching.capabilities?.sign) {
// Add both signing and verification capabilities dynamically
this._addCapability('sign', teaching.capabilities.sign);
console.debug(`Key learned signing capability from ${teaching.unitId}`);
}
if (teaching.capabilities?.verify) {
// Learn robust verification from Signer
this._addCapability('verify', teaching.capabilities.verify);
console.debug(`Key learned verification capability from ${teaching.unitId}`);
}
if (teaching.capabilities?.sign || teaching.capabilities?.verify) {
return true;
}
}
return false;
}
catch (error) {
console.error('Failed to learn capabilities:', error);
return false;
}
}
}
exports.Key = Key;