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