@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
JavaScript
/**
* 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 };
});
}