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