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.

398 lines (397 loc) 18.9 kB
/** * Signature-based group encryption * Dynamic groups with membership managed by signatures */ 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, MemberRole } from './types.js'; import { generateRandomBytes, deriveKey, encryptAES, decryptAES, signData, stringToBytes, combineBuffers, splitBuffer, generateId, KEY_SIZE, IV_SIZE, AUTH_TAG_SIZE, getCurrentTimestamp, compressData, decompressData, isValidPublicKey } from './utils.js'; /** * Default permissions for different roles */ const DEFAULT_ROLE_PERMISSIONS = { [MemberRole.OWNER]: { canDecrypt: true, canEncrypt: true, canAddMembers: true, canRemoveMembers: true, canRotateKeys: true }, [MemberRole.ADMIN]: { canDecrypt: true, canEncrypt: true, canAddMembers: true, canRemoveMembers: true, canRotateKeys: false }, [MemberRole.MEMBER]: { canDecrypt: true, canEncrypt: true, canAddMembers: false, canRemoveMembers: false, canRotateKeys: false }, [MemberRole.VIEWER]: { canDecrypt: true, canEncrypt: false, canAddMembers: false, canRemoveMembers: false, canRotateKeys: false } }; /** * Create a signature-based dynamic group */ export function createSignatureGroup(groupName, creatorPrivateKey, initialMembers, permissions) { 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 group ID const groupId = generateId(groupName, creatorPublicKey, Date.now().toString()); // Create group data for signature const groupData = { groupId, groupName, creatorPublicKey, timestamp: getCurrentTimestamp() }; // Sign group creation const groupSignature = encodeBase58(signData(stringToBytes(JSON.stringify(groupData)), creatorPrivKeyBytes)); // Generate initial master key const masterKey = generateRandomBytes(KEY_SIZE); const masterKeyId = generateId(masterKey); // Create initial epoch const initialEpoch = { epochNumber: 0, startTime: getCurrentTimestamp(), masterKeyId, rotationReason: 'Group creation' }; // Create members list with creator as owner const members = [ { publicKey: creatorPublicKey, role: MemberRole.OWNER, joinedAt: getCurrentTimestamp(), addedBy: creatorPublicKey, permissions: DEFAULT_ROLE_PERMISSIONS[MemberRole.OWNER] } ]; // Add initial members const keyShares = []; // Create key share for creator const creatorShare = yield createKeyShareForMember(masterKey, creatorPublicKey, creatorPrivKeyBytes); keyShares.push(creatorShare); // Add other initial members for (const member of initialMembers) { if (!isValidPublicKey(member.publicKey)) { throw new Error(`Invalid public key: ${member.publicKey}`); } if (member.publicKey !== creatorPublicKey) { members.push({ publicKey: member.publicKey, role: member.role, joinedAt: getCurrentTimestamp(), addedBy: creatorPublicKey, permissions: DEFAULT_ROLE_PERMISSIONS[member.role] }); const share = yield createKeyShareForMember(masterKey, member.publicKey, creatorPrivKeyBytes); keyShares.push(share); } } // Set group permissions const groupPermissions = Object.assign({ allowDynamicMembership: true, requireSignatureVerification: true, maxMembers: 100, allowKeyRotation: true, autoExpireInactiveMembers: false, inactivityThresholdDays: 90 }, permissions); return { groupId, groupName, groupSignature, members, permissions: groupPermissions, epochs: [initialEpoch], keyShares, creatorPublicKey, nonce: '', timestamp: getCurrentTimestamp(), version: '1.0.0' }; }); } /** * Add a member to a signature group */ export function addMemberToSignatureGroup(groupMetadata, newMember, authorizedMemberPrivateKey, authorizedMemberPublicKey) { return __awaiter(this, void 0, void 0, function* () { // Verify authorized member has permission const authorizedMember = groupMetadata.members.find(m => m.publicKey === authorizedMemberPublicKey); if (!authorizedMember) { throw new Error('Authorized member not found in group'); } if (!authorizedMember.permissions.canAddMembers) { throw new Error('Member does not have permission to add new members'); } // Check group limits if (groupMetadata.permissions.maxMembers > 0 && groupMetadata.members.length >= groupMetadata.permissions.maxMembers) { throw new Error('Group has reached maximum member limit'); } // Check if member already exists if (groupMetadata.members.some(m => m.publicKey === newMember.publicKey)) { throw new Error('Member already exists in group'); } // Get current master key const authorizedPrivKeyBytes = typeof authorizedMemberPrivateKey === 'string' ? decodeBase58(authorizedMemberPrivateKey) : authorizedMemberPrivateKey; const currentEpoch = groupMetadata.epochs[groupMetadata.epochs.length - 1]; const masterKey = yield getMasterKeyForMember(groupMetadata, authorizedMemberPublicKey, authorizedPrivKeyBytes); // Create key share for new member const newKeyShare = yield createKeyShareForMember(masterKey, newMember.publicKey, authorizedPrivKeyBytes); // Create new member entry const newMemberEntry = { publicKey: newMember.publicKey, role: newMember.role, joinedAt: getCurrentTimestamp(), addedBy: authorizedMemberPublicKey, permissions: DEFAULT_ROLE_PERMISSIONS[newMember.role] }; // Sign the addition const additionData = { groupId: groupMetadata.groupId, action: 'add_member', member: newMemberEntry, timestamp: getCurrentTimestamp() }; const additionSignature = encodeBase58(signData(stringToBytes(JSON.stringify(additionData)), authorizedPrivKeyBytes)); // Return updated metadata return Object.assign(Object.assign({}, groupMetadata), { members: [...groupMetadata.members, newMemberEntry], keyShares: [...groupMetadata.keyShares, newKeyShare], timestamp: getCurrentTimestamp() }); }); } /** * Remove a member from a signature group */ export function removeMemberFromSignatureGroup(groupMetadata_1, memberToRemove_1, authorizedMemberPrivateKey_1, authorizedMemberPublicKey_1) { return __awaiter(this, arguments, void 0, function* (groupMetadata, memberToRemove, authorizedMemberPrivateKey, authorizedMemberPublicKey, rotateKeys = true) { // Verify authorized member has permission const authorizedMember = groupMetadata.members.find(m => m.publicKey === authorizedMemberPublicKey); if (!authorizedMember) { throw new Error('Authorized member not found in group'); } if (!authorizedMember.permissions.canRemoveMembers) { throw new Error('Member does not have permission to remove members'); } // Cannot remove the owner const memberToRemoveData = groupMetadata.members.find(m => m.publicKey === memberToRemove); if (!memberToRemoveData) { throw new Error('Member to remove not found in group'); } if (memberToRemoveData.role === MemberRole.OWNER) { throw new Error('Cannot remove group owner'); } // Remove member and their key share const updatedMembers = groupMetadata.members.filter(m => m.publicKey !== memberToRemove); const updatedKeyShares = groupMetadata.keyShares.filter(share => share.recipientPublicKey !== memberToRemove); let updatedMetadata = Object.assign(Object.assign({}, groupMetadata), { members: updatedMembers, keyShares: updatedKeyShares, timestamp: getCurrentTimestamp() }); // Rotate keys if requested (recommended for security) if (rotateKeys && authorizedMember.permissions.canRotateKeys) { const rotationRequest = { groupId: groupMetadata.groupId, reason: `Member removed: ${memberToRemove}`, excludeMembers: [memberToRemove] }; updatedMetadata = yield rotateGroupKeys(updatedMetadata, rotationRequest, authorizedMemberPrivateKey, authorizedMemberPublicKey); } return updatedMetadata; }); } /** * Rotate encryption keys for a group */ export function rotateGroupKeys(groupMetadata, rotationRequest, authorizedMemberPrivateKey, authorizedMemberPublicKey) { return __awaiter(this, void 0, void 0, function* () { var _a; // Verify permissions const authorizedMember = groupMetadata.members.find(m => m.publicKey === authorizedMemberPublicKey); if (!(authorizedMember === null || authorizedMember === void 0 ? void 0 : authorizedMember.permissions.canRotateKeys)) { throw new Error('Member does not have permission to rotate keys'); } if (!groupMetadata.permissions.allowKeyRotation) { throw new Error('Key rotation is not allowed for this group'); } const authorizedPrivKeyBytes = typeof authorizedMemberPrivateKey === 'string' ? decodeBase58(authorizedMemberPrivateKey) : authorizedMemberPrivateKey; // Generate new master key const newMasterKey = generateRandomBytes(KEY_SIZE); const newMasterKeyId = generateId(newMasterKey); // Close current epoch const currentEpoch = groupMetadata.epochs[groupMetadata.epochs.length - 1]; currentEpoch.endTime = getCurrentTimestamp(); // Create new epoch const newEpoch = { epochNumber: currentEpoch.epochNumber + 1, startTime: getCurrentTimestamp(), masterKeyId: newMasterKeyId, rotationReason: rotationRequest.reason }; // Create new key shares for all active members const newKeyShares = []; for (const member of groupMetadata.members) { if (!((_a = rotationRequest.excludeMembers) === null || _a === void 0 ? void 0 : _a.includes(member.publicKey))) { const share = yield createKeyShareForMember(newMasterKey, member.publicKey, authorizedPrivKeyBytes); newKeyShares.push(share); } } // Update permissions if provided const updatedPermissions = rotationRequest.newPermissions ? Object.assign(Object.assign({}, groupMetadata.permissions), rotationRequest.newPermissions) : groupMetadata.permissions; return Object.assign(Object.assign({}, groupMetadata), { keyShares: newKeyShares, epochs: [...groupMetadata.epochs, newEpoch], permissions: updatedPermissions, timestamp: getCurrentTimestamp() }); }); } /** * Encrypt data for a signature group */ export function encryptForSignatureGroup(data, groupMetadata, senderPrivateKey, senderPublicKey, options) { return __awaiter(this, void 0, void 0, function* () { // Verify sender is a member with encrypt permission const senderMember = groupMetadata.members.find(m => m.publicKey === senderPublicKey); if (!senderMember) { throw new Error('Sender is not a member of this group'); } if (!senderMember.permissions.canEncrypt) { throw new Error('Sender does not have permission to encrypt for this group'); } // Convert and prepare data let dataBytes = typeof data === 'string' ? stringToBytes(data) : data; if (options === null || options === void 0 ? void 0 : options.compress) { dataBytes = yield compressData(dataBytes); } // Get current master key const senderPrivKeyBytes = typeof senderPrivateKey === 'string' ? decodeBase58(senderPrivateKey) : senderPrivateKey; const masterKey = yield getMasterKeyForMember(groupMetadata, senderPublicKey, senderPrivKeyBytes); // Encrypt data const { encrypted, iv, authTag } = encryptAES(dataBytes, masterKey); // Create metadata signature const encryptionData = { groupId: groupMetadata.groupId, sender: senderPublicKey, timestamp: getCurrentTimestamp(), epochNumber: groupMetadata.epochs[groupMetadata.epochs.length - 1].epochNumber }; const metadataSignature = encodeBase58(signData(stringToBytes(JSON.stringify(encryptionData)), senderPrivKeyBytes)); // Combine all parts const combined = combineBuffers(decodeBase58(groupMetadata.groupId), decodeBase58(metadataSignature), iv, authTag, encrypted); // Update metadata const metadata = Object.assign(Object.assign({}, groupMetadata), { nonce: encodeBase58(iv), timestamp: getCurrentTimestamp() }); if (options === null || options === void 0 ? void 0 : options.compress) { metadata.compressed = true; } return { encryptedData: encodeBase58(combined), method: EncryptionMethod.SIGNATURE_GROUP, metadata }; }); } /** * Decrypt signature group encrypted data */ export function decryptSignatureGroupData(encryptionResult, memberPrivateKey, memberPublicKey, options) { return __awaiter(this, void 0, void 0, function* () { if (encryptionResult.method !== EncryptionMethod.SIGNATURE_GROUP) { throw new Error('Invalid encryption method for signature group decryption'); } const metadata = encryptionResult.metadata; // Verify member has decrypt permission const member = metadata.members.find(m => m.publicKey === memberPublicKey); if (!member) { throw new Error('Not a member of this group'); } if (!member.permissions.canDecrypt) { throw new Error('Member does not have permission to decrypt'); } const memberPrivKeyBytes = typeof memberPrivateKey === 'string' ? decodeBase58(memberPrivateKey) : memberPrivateKey; // Get master key const masterKey = yield getMasterKeyForMember(metadata, memberPublicKey, memberPrivKeyBytes); // Decode combined data const combined = decodeBase58(encryptionResult.encryptedData); const groupIdSize = 32; const signatureSize = 64; const [groupIdBytes, signature, iv, authTag, encrypted] = splitBuffer(combined, groupIdSize, signatureSize, IV_SIZE, AUTH_TAG_SIZE); // Verify group ID if (encodeBase58(groupIdBytes) !== metadata.groupId) { throw new Error('Group ID mismatch'); } // Verify signature if requested if ((options === null || options === void 0 ? void 0 : options.verifySignature) && metadata.permissions.requireSignatureVerification) { // Would need sender's public key from metadata to verify // This is a simplified version } // Decrypt data let decrypted = decryptAES(encrypted, masterKey, iv, authTag); // Decompress if needed if (metadata.compressed) { decrypted = yield decompressData(decrypted); } return decrypted; }); } /** * Helper: Create key share for a member */ function createKeyShareForMember(masterKey, recipientPublicKey, senderPrivateKey) { return __awaiter(this, void 0, void 0, function* () { const recipientPubKeyBytes = decodeBase58(recipientPublicKey); // Use salt-based approach like direct encryption const salt = generateRandomBytes(32); const sharedSecret = deriveKey(recipientPubKeyBytes, salt, 1000); // Encrypt master key const { encrypted, iv, authTag } = encryptAES(masterKey, sharedSecret); const combined = combineBuffers(salt, iv, authTag, encrypted); return { recipientPublicKey, encryptedShare: encodeBase58(combined), createdAt: getCurrentTimestamp() }; }); } /** * Helper: Get master key for a member */ function getMasterKeyForMember(metadata, memberPublicKey, memberPrivateKey) { return __awaiter(this, void 0, void 0, function* () { // Find member's key share const keyShare = metadata.keyShares.find(share => share.recipientPublicKey === memberPublicKey); if (!keyShare) { throw new Error('No key share found for member'); } // Check expiration if (keyShare.expiresAt && getCurrentTimestamp() > keyShare.expiresAt) { throw new Error('Key share has expired'); } // Decode share const combined = decodeBase58(keyShare.encryptedShare); const [salt, iv, authTag, encrypted] = splitBuffer(combined, 32, IV_SIZE, AUTH_TAG_SIZE); // Use salt-based key derivation like in createKeyShareForMember const memberPubKeyBytes = decodeBase58(memberPublicKey); const sharedSecret = deriveKey(memberPubKeyBytes, salt, 1000); // Decrypt master key return decryptAES(encrypted, sharedSecret, iv, authTag); }); }