UNPKG

@gorbchain-xyz/chaindecode

Version:

GorbchainSDK V1.3+ - Complete Solana development toolkit with advanced cryptography, messaging, and collaboration features. Build secure applications with blockchain, DeFi, and end-to-end encryption.

297 lines (296 loc) 15.2 kB
/** * Scalable Encryption System * Seamlessly transitions from single recipient to multi-recipient encryption */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { base58ToBytes as decodeBase58 } from '../utils/base58.js'; import { Keypair } from '@solana/web3.js'; import { EncryptionMethod } from './types.js'; import { SharedKeyManager } from './shared-key-manager.js'; import { encryptDirect, decryptDirect } from './direct.js'; import { getCurrentTimestamp, isValidPublicKey } from './utils.js'; /** * Scalable encryption manager that automatically handles growth from single to multi-recipient */ export class ScalableEncryptionManager { constructor(sharedKeyManager, defaultConfig) { this.contexts = new Map(); this.sharedKeyManager = sharedKeyManager || new SharedKeyManager(); this.defaultConfig = Object.assign({ autoTransitionThreshold: 3, defaultRecipientPermissions: { canDecrypt: true, canEncrypt: true, // Enable encryption for scalable contexts canShare: false, canRevoke: false }, enableAutoKeyRotation: false, keyRotationInterval: 30 * 24 * 60 * 60 }, defaultConfig); } /** * Create a new encryption context (starts with single recipient) */ createEncryptionContext(contextName, purpose, initialRecipient, creatorPrivateKey, config) { return __awaiter(this, void 0, void 0, function* () { if (!isValidPublicKey(initialRecipient)) { throw new Error('Invalid initial recipient public key'); } const creatorPrivKeyBytes = typeof creatorPrivateKey === 'string' ? decodeBase58(creatorPrivateKey) : creatorPrivateKey; const creatorKeypair = Keypair.fromSecretKey(creatorPrivKeyBytes); const creatorPublicKey = creatorKeypair.publicKey.toBase58(); const contextId = `context_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const context = { contextId, recipients: [initialRecipient], method: EncryptionMethod.DIRECT, metadata: { name: contextName, purpose, creator: creatorPublicKey, createdAt: getCurrentTimestamp(), lastUpdated: getCurrentTimestamp() }, scalingConfig: Object.assign(Object.assign({}, this.defaultConfig), config) }; this.contexts.set(contextId, context); return context; }); } /** * Encrypt data within a context (automatically scales as needed) */ encryptInContext(contextId, data, senderPrivateKey, options) { return __awaiter(this, void 0, void 0, function* () { const context = this.contexts.get(contextId); if (!context) { throw new Error('Encryption context not found'); } const senderPrivKeyBytes = typeof senderPrivateKey === 'string' ? decodeBase58(senderPrivateKey) : senderPrivateKey; const senderKeypair = Keypair.fromSecretKey(senderPrivKeyBytes); const senderPublicKey = senderKeypair.publicKey.toBase58(); // Check if we need to transition to shared key encryption if (context.method === EncryptionMethod.DIRECT && context.recipients.length >= context.scalingConfig.autoTransitionThreshold) { yield this.transitionToSharedEncryption(contextId, senderPrivateKey, senderPublicKey); // Refresh context after transition const updatedContext = this.contexts.get(contextId); return this.encryptWithSharedKey(updatedContext, data, senderPrivateKey, senderPublicKey, options); } // Use appropriate encryption method if (context.method === EncryptionMethod.DIRECT) { // Single recipient encryption return encryptDirect(data, context.recipients[0], senderPrivateKey, options); } else { // Shared key encryption return this.encryptWithSharedKey(context, data, senderPrivateKey, senderPublicKey, options); } }); } /** * Decrypt data within a context */ decryptInContext(contextId, encryptionResult, recipientPrivateKey, recipientPublicKey) { return __awaiter(this, void 0, void 0, function* () { const context = this.contexts.get(contextId); if (!context) { throw new Error('Encryption context not found'); } // Verify recipient is authorized if (!context.recipients.includes(recipientPublicKey)) { throw new Error('Recipient not authorized for this context'); } // Use appropriate decryption method if (context.method === EncryptionMethod.DIRECT) { return decryptDirect(encryptionResult, recipientPrivateKey); } else if (context.sharedKeyId) { return this.sharedKeyManager.decryptWithSharedKey(encryptionResult, recipientPrivateKey, recipientPublicKey); } else { throw new Error('Invalid encryption context state'); } }); } /** * Add recipients to an encryption context */ addRecipientsToContext(contextId, newRecipients, authorizerPrivateKey, authorizerPublicKey, permissions) { return __awaiter(this, void 0, void 0, function* () { const context = this.contexts.get(contextId); if (!context) { throw new Error('Encryption context not found'); } // Validate new recipients const validRecipients = newRecipients.filter(recipient => { return isValidPublicKey(recipient) && !context.recipients.includes(recipient); }); if (validRecipients.length === 0) { return context; // No valid new recipients } // Check if we need to transition to shared encryption first const totalRecipients = context.recipients.length + validRecipients.length; if (context.method === EncryptionMethod.DIRECT && totalRecipients > context.scalingConfig.autoTransitionThreshold) { yield this.transitionToSharedEncryption(contextId, authorizerPrivateKey, authorizerPublicKey); } // Add recipients based on current method if (context.method === EncryptionMethod.DIRECT && context.recipients.length === 1) { // Still single recipient, but we're adding more - transition to shared yield this.transitionToSharedEncryption(contextId, authorizerPrivateKey, authorizerPublicKey); } // Refresh context after potential transition const updatedContext = this.contexts.get(contextId); // Now add to shared key if (updatedContext.sharedKeyId) { const recipientPermissions = Object.assign(Object.assign({}, updatedContext.scalingConfig.defaultRecipientPermissions), permissions); const recipientsWithPermissions = validRecipients.map(publicKey => ({ publicKey, permissions: recipientPermissions })); yield this.sharedKeyManager.addRecipientsToSharedKey(updatedContext.sharedKeyId, recipientsWithPermissions, authorizerPrivateKey, authorizerPublicKey); } // Update context with new recipients const finalUpdatedContext = Object.assign(Object.assign({}, updatedContext), { recipients: [...updatedContext.recipients, ...validRecipients], metadata: Object.assign(Object.assign({}, updatedContext.metadata), { lastUpdated: getCurrentTimestamp() }) }); this.contexts.set(contextId, finalUpdatedContext); return finalUpdatedContext; }); } /** * Remove recipients from an encryption context */ removeRecipientsFromContext(contextId_1, recipientsToRemove_1, authorizerPrivateKey_1, authorizerPublicKey_1) { return __awaiter(this, arguments, void 0, function* (contextId, recipientsToRemove, authorizerPrivateKey, authorizerPublicKey, rotateKeys = true) { const context = this.contexts.get(contextId); if (!context) { throw new Error('Encryption context not found'); } // Validate recipients to remove const validRecipientsToRemove = recipientsToRemove.filter(recipient => context.recipients.includes(recipient)); if (validRecipientsToRemove.length === 0) { return context; // No valid recipients to remove } // Don't allow removing all recipients if (validRecipientsToRemove.length >= context.recipients.length) { throw new Error('Cannot remove all recipients from context'); } // Remove from shared key if using shared encryption if (context.sharedKeyId) { yield this.sharedKeyManager.removeRecipientsFromSharedKey(context.sharedKeyId, validRecipientsToRemove, authorizerPrivateKey, authorizerPublicKey, rotateKeys); } // Update context const updatedRecipients = context.recipients.filter(recipient => !validRecipientsToRemove.includes(recipient)); const updatedContext = Object.assign(Object.assign({}, context), { recipients: updatedRecipients, metadata: Object.assign(Object.assign({}, context.metadata), { lastUpdated: getCurrentTimestamp() }) }); this.contexts.set(contextId, updatedContext); return updatedContext; }); } /** * Get context information */ getContextInfo(contextId) { return this.contexts.get(contextId) || null; } /** * List all contexts */ listContexts() { return Array.from(this.contexts.values()).map(context => ({ contextId: context.contextId, name: context.metadata.name, recipients: context.recipients.length, method: context.method, createdAt: context.metadata.createdAt })); } /** * Update context configuration */ updateContextConfig(contextId, newConfig) { const context = this.contexts.get(contextId); if (!context) { return false; } const updatedContext = Object.assign(Object.assign({}, context), { scalingConfig: Object.assign(Object.assign({}, context.scalingConfig), newConfig), metadata: Object.assign(Object.assign({}, context.metadata), { lastUpdated: getCurrentTimestamp() }) }); this.contexts.set(contextId, updatedContext); return true; } // Private helper methods transitionToSharedEncryption(contextId, authorizerPrivateKey, authorizerPublicKey) { return __awaiter(this, void 0, void 0, function* () { const context = this.contexts.get(contextId); if (!context || context.method !== EncryptionMethod.DIRECT) { return; } // Create shared key with current recipients // Give creator full permissions and recipients default permissions const initialRecipients = context.recipients.map(publicKey => ({ publicKey, permissions: context.scalingConfig.defaultRecipientPermissions })); // Add the creator with full permissions if not already included // This ensures the creator can always manage their contexts if (!initialRecipients.some(r => r.publicKey === authorizerPublicKey)) { initialRecipients.push({ publicKey: authorizerPublicKey, permissions: { canDecrypt: true, canEncrypt: true, canShare: true, canRevoke: true } }); } else { // Ensure the creator has full permissions if they're already in recipients const creatorRecipient = initialRecipients.find(r => r.publicKey === authorizerPublicKey); if (creatorRecipient) { creatorRecipient.permissions = { canDecrypt: true, canEncrypt: true, canShare: true, canRevoke: true }; } } const sharedKey = yield this.sharedKeyManager.createSharedKey({ name: `Shared key for ${context.metadata.name}`, purpose: context.metadata.purpose, creator: authorizerPublicKey, algorithm: 'AES-256-GCM', derivationMethod: 'ECDH + PBKDF2', properties: { contextId, transitionedAt: getCurrentTimestamp() } }, initialRecipients, authorizerPrivateKey); // Update context const updatedContext = Object.assign(Object.assign({}, context), { method: EncryptionMethod.GROUP, sharedKeyId: sharedKey.keyId, metadata: Object.assign(Object.assign({}, context.metadata), { lastUpdated: getCurrentTimestamp() }) }); this.contexts.set(contextId, updatedContext); }); } encryptWithSharedKey(context, data, senderPrivateKey, senderPublicKey, options) { return __awaiter(this, void 0, void 0, function* () { if (!context.sharedKeyId) { throw new Error('No shared key ID in context'); } return this.sharedKeyManager.encryptWithSharedKey(data, context.sharedKeyId, senderPrivateKey, senderPublicKey); }); } } // Convenience functions export function createScalableEncryption(contextName, purpose, initialRecipient, creatorPrivateKey, config) { return __awaiter(this, void 0, void 0, function* () { const manager = new ScalableEncryptionManager(); const context = yield manager.createEncryptionContext(contextName, purpose, initialRecipient, creatorPrivateKey, config); return { manager, context }; }); }