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.

394 lines (393 loc) 19.5 kB
/** * Shared Key Manager for transitioning between single and group encryption * Manages shared encryption/decryption keys that can be distributed among group members */ 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 { bytesToBase58 as encodeBase58, base58ToBytes as decodeBase58 } from '../utils/base58.js'; import { Keypair } from '@solana/web3.js'; import { EncryptionMethod } from './types.js'; import { generateRandomBytes, encryptAES, decryptAES, stringToBytes, bytesToString, combineBuffers, splitBuffer, generateId, signData, verifySignature, KEY_SIZE, IV_SIZE, AUTH_TAG_SIZE, getCurrentTimestamp } from './utils.js'; /** * Manages shared encryption keys for flexible group encryption */ export class SharedKeyManager { constructor() { this.sharedKeys = new Map(); this.keyDerivationCache = new Map(); } /** * Create a new shared encryption key */ createSharedKey(keyMetadata, initialRecipients, creatorPrivateKey) { return __awaiter(this, void 0, void 0, function* () { const creatorPrivKeyBytes = typeof creatorPrivateKey === 'string' ? decodeBase58(creatorPrivateKey) : creatorPrivateKey; // Get creator's public key const creatorKeypair = Keypair.fromSecretKey(creatorPrivKeyBytes); const creatorPublicKey = creatorKeypair.publicKey.toBase58(); // Generate a master shared key const masterKey = generateRandomBytes(KEY_SIZE); const keyId = generateId(masterKey, creatorPublicKey); // Create encrypted shares for each recipient const encryptedShares = new Map(); const holders = []; for (const recipient of initialRecipients) { const share = yield this.createEncryptedKeyShare(masterKey, recipient.publicKey, recipient.permissions, creatorPrivKeyBytes, creatorPublicKey); encryptedShares.set(recipient.publicKey, share); holders.push(recipient.publicKey); } const sharedKey = { keyId, encryptedShares, metadata: keyMetadata, holders, createdAt: getCurrentTimestamp() }; // Store the key this.sharedKeys.set(keyId, sharedKey); return sharedKey; }); } /** * Transition from single recipient encryption to shared key encryption */ transitionToSharedKey(originalEncryptionResult, transitionRequest) { return __awaiter(this, void 0, void 0, function* () { // First, decrypt the original data (assuming the authorizer has access) const originalData = yield this.decryptWithOriginalKey(originalEncryptionResult, transitionRequest.authorizerPrivateKey); // Create new shared key metadata const keyMetadata = { name: `Shared key transitioned from ${transitionRequest.originalRecipient}`, purpose: transitionRequest.reason, creator: transitionRequest.authorizerPublicKey, algorithm: 'AES-256-GCM', derivationMethod: 'ECDH + PBKDF2', properties: { originalRecipient: transitionRequest.originalRecipient, transitionedAt: getCurrentTimestamp() } }; // Include original recipient with full permissions const allRecipients = [ { publicKey: transitionRequest.originalRecipient, permissions: { canDecrypt: true, canEncrypt: true, canShare: true, canRevoke: true } }, ...transitionRequest.newRecipients ]; // Create the shared key const sharedKey = yield this.createSharedKey(keyMetadata, allRecipients, transitionRequest.authorizerPrivateKey); // Re-encrypt the data with the shared key const reEncryptedData = yield this.encryptWithSharedKey(originalData, sharedKey.keyId, transitionRequest.authorizerPrivateKey, transitionRequest.authorizerPublicKey); return { sharedKey, reEncryptedData }; }); } /** * Add new recipients to an existing shared key */ addRecipientsToSharedKey(keyId, newRecipients, authorizerPrivateKey, authorizerPublicKey) { return __awaiter(this, void 0, void 0, function* () { const sharedKey = this.sharedKeys.get(keyId); if (!sharedKey) { throw new Error('Shared key not found'); } // Verify authorizer has sharing permissions const authorizerShare = sharedKey.encryptedShares.get(authorizerPublicKey); if (!authorizerShare || !authorizerShare.permissions.canShare) { throw new Error('Authorizer does not have permission to share this key'); } const authorizerPrivKeyBytes = typeof authorizerPrivateKey === 'string' ? decodeBase58(authorizerPrivateKey) : authorizerPrivateKey; // First, decrypt the master key using authorizer's share const masterKey = yield this.decryptKeyShare(authorizerShare, authorizerPrivKeyBytes, authorizerShare.createdBy); // Create encrypted shares for new recipients const updatedShares = new Map(sharedKey.encryptedShares); const updatedHolders = [...sharedKey.holders]; for (const recipient of newRecipients) { if (updatedShares.has(recipient.publicKey)) { continue; // Skip if already a recipient } const share = yield this.createEncryptedKeyShare(masterKey, recipient.publicKey, recipient.permissions, authorizerPrivKeyBytes, authorizerPublicKey); updatedShares.set(recipient.publicKey, share); updatedHolders.push(recipient.publicKey); } // Update the shared key const updatedSharedKey = Object.assign(Object.assign({}, sharedKey), { encryptedShares: updatedShares, holders: updatedHolders }); this.sharedKeys.set(keyId, updatedSharedKey); return updatedSharedKey; }); } /** * Remove recipients from a shared key */ removeRecipientsFromSharedKey(keyId_1, recipientsToRemove_1, authorizerPrivateKey_1, authorizerPublicKey_1) { return __awaiter(this, arguments, void 0, function* (keyId, recipientsToRemove, authorizerPrivateKey, authorizerPublicKey, rotateKey = true) { const sharedKey = this.sharedKeys.get(keyId); if (!sharedKey) { throw new Error('Shared key not found'); } // Verify authorizer permissions const authorizerShare = sharedKey.encryptedShares.get(authorizerPublicKey); if (!authorizerShare || !authorizerShare.permissions.canShare) { throw new Error('Authorizer does not have permission to modify this key'); } // Remove specified recipients const updatedShares = new Map(sharedKey.encryptedShares); const updatedHolders = sharedKey.holders.filter(holder => !recipientsToRemove.includes(holder)); recipientsToRemove.forEach(recipient => { updatedShares.delete(recipient); }); let finalShares = updatedShares; // Rotate key if requested (recommended for security) if (rotateKey) { const authorizerPrivKeyBytes = typeof authorizerPrivateKey === 'string' ? decodeBase58(authorizerPrivateKey) : authorizerPrivateKey; // Generate new master key const newMasterKey = generateRandomBytes(KEY_SIZE); // Re-encrypt for remaining recipients finalShares = new Map(); for (const holder of updatedHolders) { const originalShare = updatedShares.get(holder); if (originalShare) { const newShare = yield this.createEncryptedKeyShare(newMasterKey, holder, originalShare.permissions, authorizerPrivKeyBytes, authorizerPublicKey); finalShares.set(holder, newShare); } } } const updatedSharedKey = Object.assign(Object.assign({}, sharedKey), { encryptedShares: finalShares, holders: updatedHolders }); this.sharedKeys.set(keyId, updatedSharedKey); return updatedSharedKey; }); } /** * Encrypt data using a shared key */ encryptWithSharedKey(data, keyId, senderPrivateKey, senderPublicKey) { return __awaiter(this, void 0, void 0, function* () { const sharedKey = this.sharedKeys.get(keyId); if (!sharedKey) { throw new Error('Shared key not found'); } // Verify sender has encryption permissions const senderShare = sharedKey.encryptedShares.get(senderPublicKey); if (!senderShare || !senderShare.permissions.canEncrypt) { throw new Error('Sender does not have permission to encrypt with this key'); } const senderPrivKeyBytes = typeof senderPrivateKey === 'string' ? decodeBase58(senderPrivateKey) : senderPrivateKey; // Decrypt the master key const masterKey = yield this.decryptKeyShare(senderShare, senderPrivKeyBytes, senderShare.createdBy); // Prepare data const dataBytes = typeof data === 'string' ? stringToBytes(data) : data; // Encrypt with master key const { encrypted, iv, authTag } = encryptAES(dataBytes, masterKey); // Create signature for integrity const timestamp = getCurrentTimestamp(); const encryptionMetadata = { keyId, sender: senderPublicKey, timestamp: timestamp, recipients: sharedKey.holders }; const metadataSignature = encodeBase58(signData(stringToBytes(JSON.stringify(encryptionMetadata)), senderPrivKeyBytes)); // Combine all parts const combined = combineBuffers(decodeBase58(keyId), decodeBase58(metadataSignature), iv, authTag, encrypted); return { encryptedData: encodeBase58(combined), method: EncryptionMethod.GROUP, metadata: { nonce: encodeBase58(iv), timestamp: timestamp, version: '2.0.0', keyId, sender: senderPublicKey, recipients: sharedKey.holders, signature: metadataSignature } }; }); } /** * Decrypt data encrypted with a shared key */ decryptWithSharedKey(encryptionResult, recipientPrivateKey, recipientPublicKey) { return __awaiter(this, void 0, void 0, function* () { const metadata = encryptionResult.metadata; const keyId = metadata.keyId; const sharedKey = this.sharedKeys.get(keyId); if (!sharedKey) { throw new Error('Shared key not found'); } // Verify recipient has decryption permissions const recipientShare = sharedKey.encryptedShares.get(recipientPublicKey); if (!recipientShare || !recipientShare.permissions.canDecrypt) { throw new Error('Recipient does not have permission to decrypt with this key'); } const recipientPrivKeyBytes = typeof recipientPrivateKey === 'string' ? decodeBase58(recipientPrivateKey) : recipientPrivateKey; // Decrypt the master key const masterKey = yield this.decryptKeyShare(recipientShare, recipientPrivKeyBytes, recipientShare.createdBy); // Decode the combined data const combined = decodeBase58(encryptionResult.encryptedData); const keyIdSize = 32; // SHA256 hash size const signatureSize = 64; // ed25519 signature size const [keyIdBytes, signature, iv, authTag, encrypted] = splitBuffer(combined, keyIdSize, signatureSize, IV_SIZE, AUTH_TAG_SIZE); // Verify key ID if (encodeBase58(keyIdBytes) !== keyId) { throw new Error('Key ID mismatch'); } // Verify signature if sender is known if (metadata.sender && metadata.signature) { const expectedMetadata = { keyId, sender: metadata.sender, timestamp: metadata.timestamp, recipients: sharedKey.holders }; const isValidSignature = verifySignature(stringToBytes(JSON.stringify(expectedMetadata)), decodeBase58(metadata.signature), decodeBase58(metadata.sender)); if (!isValidSignature) { throw new Error('Invalid encryption signature'); } } // Decrypt the data return decryptAES(encrypted, masterKey, iv, authTag); }); } /** * List all shared keys */ listSharedKeys() { return Array.from(this.sharedKeys.values()).map(key => ({ keyId: key.keyId, name: key.metadata.name, holders: key.holders.length, createdAt: key.createdAt })); } /** * Get detailed information about a shared key */ getSharedKeyInfo(keyId) { return this.sharedKeys.get(keyId) || null; } /** * Export a shared key (encrypted for backup) */ exportSharedKey(keyId, exporterPrivateKey, exporterPublicKey, backupPassword) { return __awaiter(this, void 0, void 0, function* () { const sharedKey = this.sharedKeys.get(keyId); if (!sharedKey) { throw new Error('Shared key not found'); } // Verify exporter has access const exporterShare = sharedKey.encryptedShares.get(exporterPublicKey); if (!exporterShare) { throw new Error('Exporter does not have access to this key'); } const exportData = { sharedKey, exportedAt: getCurrentTimestamp(), exportedBy: exporterPublicKey }; // Encrypt with backup password const passwordKey = new TextEncoder().encode(backupPassword).slice(0, 32); const padded = new Uint8Array(32); padded.set(passwordKey); const { encrypted, iv, authTag } = encryptAES(stringToBytes(JSON.stringify(exportData)), padded); const exportPackage = { encrypted: encodeBase58(encrypted), iv: encodeBase58(iv), authTag: encodeBase58(authTag), version: '2.0.0' }; return encodeBase58(stringToBytes(JSON.stringify(exportPackage))); }); } /** * Import a shared key from backup */ importSharedKey(exportedData, backupPassword) { return __awaiter(this, void 0, void 0, function* () { try { const exportPackage = JSON.parse(bytesToString(decodeBase58(exportedData))); // Decrypt with backup password const passwordKey = new TextEncoder().encode(backupPassword).slice(0, 32); const padded = new Uint8Array(32); padded.set(passwordKey); const decrypted = decryptAES(decodeBase58(exportPackage.encrypted), padded, decodeBase58(exportPackage.iv), decodeBase58(exportPackage.authTag)); const exportData = JSON.parse(bytesToString(decrypted)); const sharedKey = exportData.sharedKey; // Store the imported key this.sharedKeys.set(sharedKey.keyId, sharedKey); return sharedKey; } catch (error) { throw new Error('Failed to import shared key: Invalid data or password'); } }); } // Private helper methods createEncryptedKeyShare(masterKey, recipientPublicKey, permissions, senderPrivateKey, senderPublicKey) { return __awaiter(this, void 0, void 0, function* () { const recipientPubKeyBytes = decodeBase58(recipientPublicKey); // Use salt-based approach like in direct encryption const salt = generateRandomBytes(32); const { deriveKey } = yield import('./utils.js'); const sharedSecret = deriveKey(recipientPubKeyBytes, salt, 1000); // Encrypt master key with shared secret const { encrypted, iv, authTag } = encryptAES(masterKey, sharedSecret); // Combine salt, iv, authTag, and encrypted key const combined = combineBuffers(salt, iv, authTag, encrypted); return { recipientPublicKey, encryptedData: encodeBase58(combined), nonce: encodeBase58(iv), createdAt: getCurrentTimestamp(), createdBy: senderPublicKey, permissions }; }); } decryptKeyShare(keyShare, recipientPrivateKey, senderPublicKey) { return __awaiter(this, void 0, void 0, function* () { const recipientPubKeyBytes = decodeBase58(keyShare.recipientPublicKey); // Decode the encrypted share const combined = decodeBase58(keyShare.encryptedData); const [salt, iv, authTag, encrypted] = splitBuffer(combined, 32, IV_SIZE, AUTH_TAG_SIZE); // Use same salt-based approach as encryption const { deriveKey } = yield import('./utils.js'); const sharedSecret = deriveKey(recipientPubKeyBytes, salt, 1000); // Decrypt the master key return decryptAES(encrypted, sharedSecret, iv, authTag); }); } decryptWithOriginalKey(encryptionResult, privateKey) { return __awaiter(this, void 0, void 0, function* () { // This would use the appropriate decryption method based on the original encryption method // For now, assuming it's a direct encryption const { decryptDirect } = yield import('./direct.js'); return decryptDirect(encryptionResult, privateKey); }); } }