UNPKG

@opendatalabs/vana-sdk

Version:

A TypeScript library for interacting with Vana Network smart contracts.

403 lines 12.8 kB
import { getPGPKeyGenParams } from "./shared/pgp-utils.js"; import { wrapCryptoError } from "./shared/error-utils.js"; import { streamToUint8Array } from "./shared/stream-utils.js"; import { lazyImport } from "../utils/lazy-import.js"; import { WalletKeyEncryptionService } from "../crypto/services/WalletKeyEncryptionService.js"; import { processWalletPrivateKey, parseEncryptedDataBuffer, processWalletPublicKey } from "../utils/crypto-utils.js"; const getOpenPGP = lazyImport(() => import("openpgp")); import { NodeECIESUint8Provider } from "../crypto/ecies/node.js"; import { ECIESError } from "../crypto/ecies/interface.js"; import { randomBytes } from "crypto"; import secp256k1Import from "secp256k1"; class NodeCryptoAdapter { eciesProvider = new NodeECIESUint8Provider(); 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. * Obtain from key generation or user profile. * @returns Encrypted data as a hex string containing IV, ephemeral key, ciphertext, and MAC * * @throws {Error} If encryption fails or public key is invalid */ async encryptWithPublicKey(data, publicKeyHex) { try { const publicKeyBytes = processWalletPublicKey(publicKeyHex); const publicKey = Buffer.from(publicKeyBytes); const message = Buffer.from(data, "utf8"); const encrypted = await this.eciesProvider.encrypt(publicKey, message); const result = Buffer.concat([ encrypted.iv, encrypted.ephemPublicKey, encrypted.ciphertext, encrypted.mac ]); return result.toString("hex"); } catch (error) { if (error instanceof ECIESError) { throw error; } throw new Error( `Encryption failed: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Decrypts ECIES-encrypted data using a private key. * * @param encryptedData - Hex string containing encrypted data. * Must include IV, ephemeral public key, ciphertext, and MAC. * @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 * @throws {ECIESError} If using custom ECIES and specific error occurs */ async decryptWithPrivateKey(encryptedData, privateKeyHex) { try { const privateKeyBuffer = processWalletPrivateKey(privateKeyHex); const encryptedHex = encryptedData.startsWith("0x") ? encryptedData.slice(2) : encryptedData; const encryptedBuffer = Buffer.from(encryptedHex, "hex"); const { iv, ephemPublicKey, ciphertext, mac } = parseEncryptedDataBuffer(encryptedBuffer); const encryptedObj = { iv, ephemPublicKey, ciphertext, mac }; const decrypted = await this.eciesProvider.decrypt( privateKeyBuffer, encryptedObj ); return new TextDecoder().decode(decrypted); } catch (error) { if (error instanceof ECIESError) { throw error; } throw new Error( `Decryption failed: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Generates a new secp256k1 key pair for ECIES operations. * * @returns Object containing hex-encoded public and private keys * @returns returns.publicKey - Compressed public key in hex format * @returns returns.privateKey - Private key in hex format * * @throws {Error} If key generation fails */ async generateKeyPair() { try { const secp256k1 = secp256k1Import; let privateKey; do { privateKey = randomBytes(32); } while (!secp256k1.privateKeyVerify(privateKey)); const publicKey = Buffer.from( secp256k1.publicKeyCreate(privateKey, true) ); return { privateKey: privateKey.toString("hex"), publicKey: publicKey.toString("hex") }; } catch (error) { throw wrapCryptoError("key generation", 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 (with or without 0x prefix). * Obtain from wallet connection or user profile. * @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("encrypt with wallet public key", 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("decrypt with wallet private key", 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. Note that this is not * deterministic due to OpenPGP's random salt generation. * * @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" }); if (encrypted instanceof Uint8Array) { return encrypted; } if (encrypted && typeof encrypted === "object" && "getReader" in encrypted) { return await streamToUint8Array( encrypted ); } throw new Error("Unexpected encrypted data format"); } catch (error) { throw wrapCryptoError("encrypt with password", 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: decrypted } = await openpgp.decrypt({ message, passwords: [password], format: "binary" }); return new Uint8Array(decrypted); } catch (error) { throw wrapCryptoError("decrypt with password", error); } } } class NodePGPAdapter { /** * 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 wrapCryptoError("PGP encryption", 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 wrapCryptoError("PGP decryption", 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 NodeHttpAdapter { /** * Performs an HTTP request using fetch. * * @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 * * @throws {Error} If fetch is not available in the environment */ async fetch(url, options) { if (typeof globalThis.fetch !== "undefined") { return globalThis.fetch(url, options); } throw new Error("No fetch implementation available in Node.js environment"); } } class NodeCacheAdapter { cache = /* @__PURE__ */ new Map(); defaultTtl = 2 * 60 * 60 * 1e3; // 2 hours in milliseconds /** * Retrieves a cached value by key. * * @param key - The cache key to look up. * Typically derived from operation parameters. * @returns The cached value or null if not found/expired */ get(key) { const entry = this.cache.get(key); if (!entry) { return null; } if (Date.now() > entry.expires) { this.cache.delete(key); return null; } return entry.value; } /** * Stores a value in the cache with TTL. * * @param key - The cache key. * Should be unique per operation. * @param value - The value to cache. * Typically serialized data or signatures. */ set(key, value) { this.cache.set(key, { value, expires: Date.now() + this.defaultTtl }); } /** * Removes a specific key from the cache. * * @param key - The cache key to remove. * Use when cached data becomes invalid. */ delete(key) { this.cache.delete(key); } /** * Clears all cached values. * * @remarks * Use with caution as this removes all cached signatures * and other performance optimizations. */ clear() { this.cache.clear(); } } class NodePlatformAdapter { crypto; pgp; http; cache; platform = "node"; constructor() { this.crypto = new NodeCryptoAdapter(); this.pgp = new NodePGPAdapter(); this.http = new NodeHttpAdapter(); this.cache = new NodeCacheAdapter(); } } const nodePlatformAdapter = new NodePlatformAdapter(); export { NodePlatformAdapter, nodePlatformAdapter }; //# sourceMappingURL=node.js.map