UNPKG

@keypo/synapse-storage-sdk

Version:
242 lines (241 loc) 8.95 kB
/** * Lit Protocol encryption module for Synapse Storage SDK */ import { createLitClient } from '@lit-protocol/lit-client'; import { createAuthManager, storagePlugins } from "@lit-protocol/auth"; import { nagaTest } from '@lit-protocol/networks'; import { privateKeyToAccount } from 'viem/accounts'; import { createWalletClient, http } from 'viem'; import { baseSepolia } from 'viem/chains'; import { KernelVersionToAddressesMap, KERNEL_V3_3 } from '@zerodev/sdk/constants'; import { generateRandomDataIdentifier } from '../../utils/identifiers.js'; import { createEncryptionError } from '../../errors/index.js'; export const TIME = { /** Milliseconds in one second */ SECOND_MS: 1000, /** Milliseconds in one minute */ MINUTE_MS: 60 * 1000, /** Milliseconds in one hour */ HOUR_MS: 60 * 60 * 1000, /** Milliseconds in one day */ DAY_MS: 24 * 60 * 60 * 1000, /** Timestamp conversion factor (seconds to milliseconds) */ TIMESTAMP_TO_MS: 1000, }; export const AUTH_EXPIRATION = { /** Default auth expiration time (24 hours in milliseconds) */ DEFAULT_MS: TIME.DAY_MS, }; export class LitEncryption { config; litClient = null; account = null; authManager = null; constructor(config) { this.config = config; // Initialize auth manager once for shared usage this.authManager = createAuthManager({ storage: storagePlugins.localStorageNode({ appName: 'synapse-cli', networkName: 'naga-test', storagePath: './lit-auth-local', }), }); } /** * Initialize Lit Protocol client */ async initializeLitClient() { if (this.litClient) return this.litClient; try { this.litClient = await createLitClient({ network: nagaTest, }); return this.litClient; } catch (error) { throw createEncryptionError('Failed to initialize Lit Protocol client', { cause: error, userMessage: 'Could not connect to Lit Protocol network' }); } } /** * Get or create account from private key */ getAccount() { if (this.account) return this.account; const formattedPrivateKey = this.config.privateKey.startsWith('0x') ? this.config.privateKey : `0x${this.config.privateKey}`; this.account = privateKeyToAccount(formattedPrivateKey); return this.account; } /** * Create authentication context for Lit Protocol operations */ async createAuthContext(litClient, operation) { return await this.authManager.createEoaAuthContext({ config: { account: this.getAccount(), }, authConfig: { domain: 'localhost', statement: `Synapse Storage SDK ${operation}`, expiration: new Date(Date.now() + AUTH_EXPIRATION.DEFAULT_MS).toISOString(), resources: [ ['access-control-condition-decryption', '*'], ['lit-action-execution', '*'], ], }, litClient, }); } /** * Create access control conditions for the encrypted data */ createAccessControlConditions(dataIdentifier) { return [{ contractAddress: this.config.registryAddress, functionName: "checkPermission", functionParams: [dataIdentifier, ":userAddress"], functionAbi: { type: "function", stateMutability: "view", outputs: [ { type: "bool", name: "", internalType: "bool", }, ], name: "checkPermission", inputs: [ { type: "string", name: "fileIdentifier", internalType: "string", }, { type: "address", name: "requestAddress", internalType: "address", }, ], }, chain: "baseSepolia", conditionType: "evmContract", returnValueTest: { key: "", comparator: "=", value: "true", }, }]; } /** * Encrypt data with Lit Protocol */ async encrypt(data, metadata) { try { // Initialize Lit client const litClient = await this.initializeLitClient(); // Get account const account = this.getAccount(); // Generate unique data identifier const dataIdentifier = generateRandomDataIdentifier(data); // Create access control conditions const accessControlConditions = this.createAccessControlConditions(dataIdentifier); // Create authentication context for encryption const authContext = await this.createAuthContext(litClient, 'encryption'); // Encrypt the data const encryptedData = await litClient.encrypt({ dataToEncrypt: data, unifiedAccessControlConditions: accessControlConditions, chain: 'baseSepolia', authContext: authContext, }); // Create wallet client for signing const walletClient = createWalletClient({ account, chain: baseSepolia, transport: http(), }); // Get kernel authorization for smart contract operations const kernelVersion = KERNEL_V3_3; const kernelAddresses = KernelVersionToAddressesMap[kernelVersion]; const accountImplementationAddress = kernelAddresses.accountImplementationAddress; const authorization = await walletClient.signAuthorization({ contractAddress: accountImplementationAddress, account, }); // Return encrypted payload with all necessary data return { ciphertext: encryptedData.ciphertext, dataToEncryptHash: encryptedData.dataToEncryptHash, accessControlConditions, metadata: metadata, dataIdentifier, smartContractData: { kernelClient: { authorization, account }, // Simplified for now userAddress: account.address, registryContractAddress: this.config.registryAddress, validationContractAddress: this.config.validationAddress, } }; } catch (error) { throw createEncryptionError('Encryption failed', { cause: error, userMessage: 'Failed to encrypt data with Lit Protocol' }); } } /** * Decrypt data with Lit Protocol */ async decrypt(encryptedPayload) { try { // Initialize Lit client const litClient = await this.initializeLitClient(); // Get access control conditions const accs = encryptedPayload.accessControlConditions; // Create authentication context for decryption const authContext = await this.createAuthContext(litClient, 'decryption'); // Prepare encrypted data for decryption const encryptedData = { ciphertext: encryptedPayload.ciphertext, dataToEncryptHash: encryptedPayload.dataToEncryptHash, }; // Perform decryption const decryptedResponse = await litClient.decrypt({ data: encryptedData, unifiedAccessControlConditions: accs, authContext: authContext, chain: 'baseSepolia', }); return decryptedResponse.decryptedData; } catch (error) { throw createEncryptionError('Decryption failed', { cause: error, userMessage: 'Failed to decrypt data. You may not have permission to access this file.' }); } } /** * Clean up resources */ async cleanup() { if (this.litClient) { try { await this.litClient.disconnect(); } catch (error) { console.warn('Failed to disconnect Lit client:', error); } this.litClient = null; } } }