UNPKG

@opendatalabs/vana-sdk

Version:

A TypeScript library for interacting with Vana Network smart contracts.

375 lines 12 kB
import { getPGPKeyGenParams } from "./shared/pgp-utils.js"; import { wrapCryptoError } from "./shared/error-utils.js"; import { lazyImport } from "../utils/lazy-import.js"; import { WalletKeyEncryptionService } from "../crypto/services/WalletKeyEncryptionService.js"; import { parseEncryptedDataBuffer } from "../utils/crypto-utils.js"; import { toHex, fromHex, stringToBytes, bytesToString, concat } from "viem"; import * as secp256k1 from "@noble/secp256k1"; import { BrowserECIESUint8Provider } from "../crypto/ecies/browser.js"; const getOpenPGP = lazyImport(() => import("openpgp")); class BrowserCryptoAdapter { eciesProvider = new BrowserECIESUint8Provider(); walletService = new WalletKeyEncryptionService({ eciesProvider: this.eciesProvider }); /** * Encrypts data using ECIES with a public key. * * @param data - The plaintext string to encrypt. * Typically user data or sensitive information. * @param publicKeyHex - The recipient's public key in hex format. * Can include or omit the '0x' prefix. * @returns Encrypted data as a hex string without '0x' prefix * * @throws {Error} If encryption fails or public key is invalid */ async encryptWithPublicKey(data, publicKeyHex) { try { const prefixedHex = publicKeyHex.startsWith("0x") ? publicKeyHex : `0x${publicKeyHex}`; const publicKeyBytes = fromHex(prefixedHex, "bytes"); const encrypted = await this.eciesProvider.encrypt( publicKeyBytes, stringToBytes(data) ); const result = concat([ encrypted.iv, encrypted.ephemPublicKey, encrypted.ciphertext, encrypted.mac ]); return toHex(result).slice(2); } catch (error) { throw wrapCryptoError("encryptWithPublicKey", error); } } /** * Decrypts ECIES-encrypted data using a private key. * * @param encryptedData - Hex string containing encrypted data. * Can include or omit the '0x' prefix. * @param privateKeyHex - The private key in hex format. * Must correspond to the public key used for encryption. * @returns The decrypted plaintext string * * @throws {Error} If decryption fails or MAC verification fails */ async decryptWithPrivateKey(encryptedData, privateKeyHex) { try { const encryptedHex = encryptedData.startsWith("0x") ? encryptedData : `0x${encryptedData}`; const privateHex = privateKeyHex.startsWith("0x") ? privateKeyHex : `0x${privateKeyHex}`; const encryptedBytes = fromHex(encryptedHex, "bytes"); const privateKeyBytes = fromHex(privateHex, "bytes"); const encrypted = parseEncryptedDataBuffer(encryptedBytes); const decrypted = await this.eciesProvider.decrypt( privateKeyBytes, encrypted ); return bytesToString(decrypted); } catch (error) { throw wrapCryptoError("decryptWithPrivateKey", error); } } /** * Encrypts data using a wallet's public key. * * @param data - The plaintext string to encrypt. * Typically permission data or DLP metadata. * @param publicKey - The wallet's public key. * Automatically handles compressed/uncompressed formats. * @returns Encrypted data as a hex string * * @throws {Error} If encryption fails or key processing fails */ async encryptWithWalletPublicKey(data, publicKey) { try { return await this.walletService.encryptWithWalletPublicKey( data, publicKey ); } catch (error) { throw wrapCryptoError("encryptWithWalletPublicKey", error); } } /** * Decrypts data using a wallet's private key. * * @param encryptedData - Hex string containing encrypted data. * Must be encrypted with corresponding wallet public key. * @param privateKey - The wallet's private key. * Obtain from wallet connection (handle with care). * @returns The decrypted plaintext string * * @throws {Error} If decryption fails or key is invalid */ async decryptWithWalletPrivateKey(encryptedData, privateKey) { try { return await this.walletService.decryptWithWalletPrivateKey( encryptedData, privateKey ); } catch (error) { throw wrapCryptoError("decryptWithWalletPrivateKey", error); } } /** * Generates a new secp256k1 key pair for ECIES operations. * * @returns Object containing hex-encoded public and private keys * @returns returns.privateKey - Private key in hex format without '0x' * @returns returns.publicKey - Compressed public key in hex format without '0x' * * @throws {Error} If key generation fails */ async generateKeyPair() { try { const privateKeyBytes = secp256k1.utils.randomPrivateKey(); const publicKeyBytes = secp256k1.getPublicKey(privateKeyBytes, true); return { privateKey: toHex(privateKeyBytes).slice(2), publicKey: toHex(publicKeyBytes).slice(2) }; } catch (error) { throw wrapCryptoError("generateKeyPair", error); } } /** * Encrypts binary data using password-based encryption. * * @param data - Binary data to encrypt. * Typically file contents or serialized objects. * @param password - Password for encryption. * Often derived from wallet signatures. * @returns Encrypted data as Uint8Array * * @remarks * Uses OpenPGP for password-based encryption with automatic * salt generation for security. * * @throws {Error} If encryption fails */ async encryptWithPassword(data, password) { try { const openpgp = await getOpenPGP(); const message = await openpgp.createMessage({ binary: data }); const encrypted = await openpgp.encrypt({ message, passwords: [password], format: "binary" }); return new Uint8Array(encrypted); } catch (error) { throw wrapCryptoError("encryptWithPassword", error); } } /** * Decrypts password-encrypted binary data. * * @param encryptedData - Password-encrypted data as Uint8Array. * Must be encrypted with the same password. * @param password - Password for decryption. * Must match the encryption password. * @returns Decrypted data as Uint8Array * * @throws {Error} If decryption fails or password is incorrect */ async decryptWithPassword(encryptedData, password) { try { const openpgp = await getOpenPGP(); const message = await openpgp.readMessage({ binaryMessage: encryptedData }); const { data } = await openpgp.decrypt({ message, passwords: [password], format: "binary" }); return new Uint8Array(data); } catch (error) { throw wrapCryptoError("decryptWithPassword", error); } } } class BrowserPGPAdapter { /** * Encrypts data using PGP public key encryption. * * @param data - The plaintext string to encrypt. * Typically messages or structured data. * @param publicKeyArmored - ASCII-armored PGP public key. * Obtain from PGP key generation or key servers. * @returns ASCII-armored encrypted message * * @throws {Error} If encryption fails or public key is invalid */ async encrypt(data, publicKeyArmored) { try { const openpgp = await getOpenPGP(); const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: data }), encryptionKeys: publicKey, config: { preferredCompressionAlgorithm: openpgp.enums.compression.zlib } }); return encrypted; } catch (error) { throw new Error(`PGP encryption failed: ${String(error)}`); } } /** * Decrypts PGP-encrypted data using a private key. * * @param encryptedData - ASCII-armored encrypted message. * Must be encrypted with corresponding public key. * @param privateKeyArmored - ASCII-armored PGP private key. * Must correspond to the public key used for encryption. * @returns The decrypted plaintext string * * @throws {Error} If decryption fails or private key is invalid */ async decrypt(encryptedData, privateKeyArmored) { try { const openpgp = await getOpenPGP(); const privateKey = await openpgp.readPrivateKey({ armoredKey: privateKeyArmored }); const message = await openpgp.readMessage({ armoredMessage: encryptedData }); const { data: decrypted } = await openpgp.decrypt({ message, decryptionKeys: privateKey }); return decrypted; } catch (error) { throw new Error(`PGP decryption failed: ${String(error)}`); } } /** * Generates a new PGP key pair. * * @param options - Key generation options * @param options.name - Name for the key identity. * Defaults to 'Vana User'. * @param options.email - Email for the key identity. * Defaults to 'user@vana.com'. * @param options.passphrase - Passphrase to protect the private key. * If not provided, key is unprotected. * @returns ASCII-armored public and private keys * * @throws {Error} If key generation fails */ async generateKeyPair(options) { try { const openpgp = await getOpenPGP(); const keyGenParams = getPGPKeyGenParams(options); const { privateKey, publicKey } = await openpgp.generateKey(keyGenParams); return { publicKey, privateKey }; } catch (error) { throw wrapCryptoError("PGP key generation", error); } } } class BrowserHttpAdapter { /** * Performs an HTTP request using the Fetch API. * * @param url - The URL to fetch. * Must be a valid HTTP/HTTPS URL. * @param options - Standard fetch options. * See MDN fetch documentation for details. * @returns Standard fetch Response object */ async fetch(url, options) { return fetch(url, options); } } class BrowserCacheAdapter { prefix = "vana_cache_"; /** * Retrieves a cached value by key. * * @param key - The cache key to look up. * Automatically prefixed to avoid conflicts. * @returns The cached value or null if not found */ get(key) { try { if (typeof sessionStorage === "undefined") { return null; } return sessionStorage.getItem(this.prefix + key); } catch { return null; } } /** * Stores a value in sessionStorage. * * @param key - The cache key. * Automatically prefixed with 'vana_cache_'. * @param value - The value to cache. * Will be cleared when tab closes. */ set(key, value) { try { if (typeof sessionStorage === "undefined") { return; } sessionStorage.setItem(this.prefix + key, value); } catch { } } /** * Removes a specific key from the cache. * * @param key - The cache key to remove. * Only removes the prefixed key. */ delete(key) { try { if (typeof sessionStorage === "undefined") { return; } sessionStorage.removeItem(this.prefix + key); } catch { } } /** * Clears all Vana-prefixed cache entries. * * @remarks * Only removes entries with the 'vana_cache_' prefix, * preserving other sessionStorage data. */ clear() { try { if (typeof sessionStorage === "undefined") { return; } const keysToRemove = []; for (let i = 0; i < sessionStorage.length; i++) { const key = sessionStorage.key(i); if (key?.startsWith(this.prefix)) { keysToRemove.push(key); } } keysToRemove.forEach((key) => { sessionStorage.removeItem(key); }); } catch { } } } class BrowserPlatformAdapter { crypto = new BrowserCryptoAdapter(); pgp = new BrowserPGPAdapter(); http = new BrowserHttpAdapter(); cache = new BrowserCacheAdapter(); platform = "browser"; } export { BrowserPlatformAdapter }; //# sourceMappingURL=browser.js.map