UNPKG

@opendatalabs/vana-sdk

Version:

A TypeScript library for interacting with Vana Network smart contracts.

1,351 lines (1,338 loc) 700 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/platform/shared/pgp-utils.ts function processPGPKeyOptions(options) { return { name: options?.name ?? "Vana User", email: options?.email ?? "user@vana.org", passphrase: options?.passphrase }; } function getPGPKeyGenParams(options) { const { name, email, passphrase } = processPGPKeyOptions(options); return { type: "rsa", rsaBits: 2048, userIDs: [{ name, email }], passphrase, config: STANDARD_PGP_CONFIG }; } var STANDARD_PGP_CONFIG; var init_pgp_utils = __esm({ "src/platform/shared/pgp-utils.ts"() { "use strict"; STANDARD_PGP_CONFIG = { preferredCompressionAlgorithm: 2, // zlib (openpgp.enums.compression.zlib) preferredSymmetricAlgorithm: 7 // aes256 (openpgp.enums.symmetric.aes256) }; } }); // src/platform/shared/error-utils.ts function wrapCryptoError(operation, error) { const message = error instanceof Error ? error.message : "Unknown error"; return new Error(`${operation} failed: ${message}`); } var init_error_utils = __esm({ "src/platform/shared/error-utils.ts"() { "use strict"; } }); // src/utils/lazy-import.ts function lazyImport(importFn) { let cached = null; return () => { cached ??= importFn().catch((err) => { cached = null; throw new Error("Failed to load module", { cause: err }); }); return cached; }; } var init_lazy_import = __esm({ "src/utils/lazy-import.ts"() { "use strict"; } }); // src/utils/crypto-utils.ts function processWalletPublicKey(publicKey) { return typeof publicKey === "string" ? (0, import_viem5.fromHex)( publicKey.startsWith("0x") ? publicKey : `0x${publicKey}`, "bytes" ) : publicKey; } function processWalletPrivateKey(privateKey) { return typeof privateKey === "string" ? (0, import_viem5.fromHex)( privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`, "bytes" ) : privateKey; } function parseEncryptedDataBuffer(encryptedBuffer) { return { iv: encryptedBuffer.slice(0, 16), ephemPublicKey: encryptedBuffer.slice(16, 81), // 65 bytes for uncompressed public key ciphertext: encryptedBuffer.slice(81, -32), mac: encryptedBuffer.slice(-32) }; } var import_viem5; var init_crypto_utils = __esm({ "src/utils/crypto-utils.ts"() { "use strict"; import_viem5 = require("viem"); } }); // src/crypto/services/WalletKeyEncryptionService.ts var import_viem6, WalletKeyEncryptionService; var init_WalletKeyEncryptionService = __esm({ "src/crypto/services/WalletKeyEncryptionService.ts"() { "use strict"; init_crypto_utils(); import_viem6 = require("viem"); WalletKeyEncryptionService = class { eciesProvider; constructor(config) { this.eciesProvider = config.eciesProvider; } /** * Encrypts data using a wallet's public key. * * @param data - The plaintext message to encrypt for the wallet owner. * @param publicKey - The recipient wallet's public key for encryption. * @returns A promise that resolves to the encrypted data as a hex string. * @throws {Error} When encryption fails due to invalid key format. * * @example * ```typescript * const encrypted = await processor.encryptWithWalletPublicKey( * "Secret message", * "0x04..." // 65-byte uncompressed public key * ); * console.log(`Encrypted: ${encrypted}`); * ``` */ async encryptWithWalletPublicKey(data, publicKey) { const publicKeyBytes = processWalletPublicKey(publicKey); const normalizedKey = this.eciesProvider.normalizeToUncompressed(publicKeyBytes); const dataBytes = (0, import_viem6.stringToBytes)(data); const encrypted = await this.eciesProvider.encrypt( normalizedKey, dataBytes ); const result = (0, import_viem6.concat)([ encrypted.iv, encrypted.ephemPublicKey, encrypted.ciphertext, encrypted.mac ]); return (0, import_viem6.toHex)(result).slice(2); } /** * Decrypts data using a wallet's private key. * * @param encryptedData - The hex-encoded encrypted data to decrypt. * @param privateKey - The wallet's private key for decryption. * @returns A promise that resolves to the decrypted plaintext message. * @throws {Error} When decryption fails due to invalid data or key format. * * @example * ```typescript * const decrypted = await processor.decryptWithWalletPrivateKey( * encryptedHexString, * "0x..." // 32-byte private key * ); * console.log(`Decrypted: ${decrypted}`); * ``` */ async decryptWithWalletPrivateKey(encryptedData, privateKey) { const privateKeyBytes = processWalletPrivateKey(privateKey); const prefixedHex = encryptedData.startsWith("0x") ? encryptedData : `0x${encryptedData}`; const encryptedBytes = (0, import_viem6.fromHex)(prefixedHex, "bytes"); const encrypted = parseEncryptedDataBuffer(encryptedBytes); const decrypted = await this.eciesProvider.decrypt( privateKeyBytes, encrypted ); return (0, import_viem6.bytesToString)(decrypted); } /** * Encrypts a Uint8Array with a wallet public key * * @param data - Binary data to encrypt * @param publicKey - Public key as hex string or Uint8Array * @returns Encrypted data structure */ async encryptBinary(data, publicKey) { const publicKeyBytes = processWalletPublicKey(publicKey); const normalizedKey = this.eciesProvider.normalizeToUncompressed(publicKeyBytes); return this.eciesProvider.encrypt(normalizedKey, data); } /** * Decrypts to a Uint8Array with a wallet private key * * @param encrypted - Encrypted data structure * @param privateKey - Private key as hex string or Uint8Array * @returns Decrypted binary data */ async decryptBinary(encrypted, privateKey) { const privateKeyBytes = processWalletPrivateKey(privateKey); return this.eciesProvider.decrypt(privateKeyBytes, encrypted); } /** * Gets the underlying ECIES provider * * @returns The ECIES provider instance */ getECIESProvider() { return this.eciesProvider; } }; } }); // src/crypto/ecies/constants.ts var CURVE, CIPHER, KDF, MAC, FORMAT; var init_constants = __esm({ "src/crypto/ecies/constants.ts"() { "use strict"; CURVE = { /** The elliptic curve used (secp256k1 - same as Bitcoin/Ethereum) */ name: "secp256k1", /** Private key length in bytes */ PRIVATE_KEY_LENGTH: 32, /** Compressed public key length in bytes (0x02 or 0x03 prefix + 32 bytes) */ COMPRESSED_PUBLIC_KEY_LENGTH: 33, /** Uncompressed public key length in bytes (0x04 prefix + 64 bytes) */ UNCOMPRESSED_PUBLIC_KEY_LENGTH: 65, /** ECDH shared secret X coordinate length */ SHARED_SECRET_LENGTH: 32, /** Public key prefixes */ PREFIX: { /** Uncompressed public key prefix */ UNCOMPRESSED: 4, /** Compressed public key prefix for even Y */ COMPRESSED_EVEN: 2, /** Compressed public key prefix for odd Y */ COMPRESSED_ODD: 3 }, /** X coordinate starts at byte 1 (after prefix) */ X_COORDINATE_OFFSET: 1, /** X coordinate ends at byte 33 (1 + 32) */ X_COORDINATE_END: 33 }; CIPHER = { /** Cipher algorithm - must match eccrypto */ algorithm: "aes-256-cbc", /** AES key length in bytes */ KEY_LENGTH: 32, /** Initialization vector length in bytes */ IV_LENGTH: 16, /** Block size for AES */ BLOCK_SIZE: 16 }; KDF = { /** Hash algorithm for key derivation - must match eccrypto */ algorithm: "sha512", /** Output length of SHA-512 in bytes */ OUTPUT_LENGTH: 64, /** Encryption key slice (first 32 bytes of KDF output) */ ENCRYPTION_KEY_OFFSET: 0, ENCRYPTION_KEY_LENGTH: 32, /** MAC key slice (last 32 bytes of KDF output) */ MAC_KEY_OFFSET: 32, MAC_KEY_LENGTH: 32 }; MAC = { /** MAC algorithm - must match eccrypto */ algorithm: "sha256", /** HMAC-SHA256 output length in bytes */ LENGTH: 32 }; FORMAT = { /** Offsets for each component in serialized format */ IV_OFFSET: 0, IV_LENGTH: CIPHER.IV_LENGTH, /** Ephemeral public key (always uncompressed in eccrypto format) */ EPHEMERAL_KEY_OFFSET: CIPHER.IV_LENGTH, EPHEMERAL_KEY_LENGTH: CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH, /** Ciphertext starts after IV and ephemeral key */ CIPHERTEXT_OFFSET: CIPHER.IV_LENGTH + CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH, /** MAC is always the last 32 bytes */ MAC_LENGTH: MAC.LENGTH, /** Minimum size of encrypted data (IV + ephemKey + MAC, no ciphertext) */ MIN_ENCRYPTED_LENGTH: CIPHER.IV_LENGTH + CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH + MAC.LENGTH, /** * Helper to calculate total length of encrypted data * * @param ciphertextLength - Length of the ciphertext portion * @returns Total length including all components */ getTotalLength: (ciphertextLength) => CIPHER.IV_LENGTH + CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH + ciphertextLength + MAC.LENGTH }; } }); // src/crypto/ecies/interface.ts function isECIESEncrypted(obj) { if (!obj || typeof obj !== "object") return false; const enc = obj; const isUint8Array = (value) => { return value instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(value); }; return isUint8Array(enc.iv) && enc.iv.length === CIPHER.IV_LENGTH && isUint8Array(enc.ephemPublicKey) && (enc.ephemPublicKey.length === CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH || enc.ephemPublicKey.length === CURVE.COMPRESSED_PUBLIC_KEY_LENGTH) && isUint8Array(enc.ciphertext) && enc.ciphertext.length > 0 && isUint8Array(enc.mac) && enc.mac.length === MAC.LENGTH; } function serializeECIES(encrypted) { const combined = new Uint8Array( encrypted.iv.length + encrypted.ephemPublicKey.length + encrypted.ciphertext.length + encrypted.mac.length ); let offset = 0; combined.set(encrypted.iv, offset); offset += encrypted.iv.length; combined.set(encrypted.ephemPublicKey, offset); offset += encrypted.ephemPublicKey.length; combined.set(encrypted.ciphertext, offset); offset += encrypted.ciphertext.length; combined.set(encrypted.mac, offset); return (0, import_viem7.toHex)(combined).slice(2); } function deserializeECIES(hex) { const hexWithPrefix = hex.startsWith("0x") ? hex : `0x${hex}`; const bytes = (0, import_viem7.fromHex)(hexWithPrefix, "bytes"); const absoluteMinLength = FORMAT.IV_LENGTH + 1 + MAC.LENGTH + 1; if (bytes.length < absoluteMinLength) { throw new ECIESError( `Invalid ECIES data: too short (${bytes.length} bytes, minimum ${absoluteMinLength} bytes required)`, "DECRYPTION_FAILED" ); } const prefix = bytes[FORMAT.EPHEMERAL_KEY_OFFSET]; if (prefix !== CURVE.PREFIX.UNCOMPRESSED) { throw new ECIESError( `Invalid ephemeral public key: must be uncompressed format (0x04 prefix), got 0x${prefix.toString(16).padStart(2, "0")}`, "DECRYPTION_FAILED" ); } const ephemKeySize = CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH; const minLength = FORMAT.IV_LENGTH + ephemKeySize + MAC.LENGTH + 1; if (bytes.length < minLength) { throw new ECIESError( `Invalid ECIES data: too short (${bytes.length} bytes, minimum ${minLength} bytes required)`, "DECRYPTION_FAILED" ); } return { iv: bytes.subarray(FORMAT.IV_OFFSET, FORMAT.IV_OFFSET + FORMAT.IV_LENGTH), ephemPublicKey: bytes.subarray( FORMAT.EPHEMERAL_KEY_OFFSET, FORMAT.EPHEMERAL_KEY_OFFSET + ephemKeySize ), ciphertext: bytes.subarray( FORMAT.EPHEMERAL_KEY_OFFSET + ephemKeySize, bytes.length - MAC.LENGTH ), mac: bytes.subarray(bytes.length - MAC.LENGTH) }; } var import_viem7, ECIESError; var init_interface = __esm({ "src/crypto/ecies/interface.ts"() { "use strict"; init_constants(); import_viem7 = require("viem"); ECIESError = class extends Error { constructor(message, code, cause) { super(message); this.code = code; this.cause = cause; this.name = "ECIESError"; } code; cause; }; } }); // src/crypto/ecies/utils.ts function constantTimeEqual(a, b) { if (a.length !== b.length) return false; let result = 0; for (let i = 0; i < a.length; i++) { result |= a[i] ^ b[i]; } return result === 0; } var init_utils = __esm({ "src/crypto/ecies/utils.ts"() { "use strict"; } }); // src/crypto/ecies/base.ts var import_viem8, BaseECIESUint8; var init_base = __esm({ "src/crypto/ecies/base.ts"() { "use strict"; init_interface(); init_constants(); init_utils(); import_viem8 = require("viem"); BaseECIESUint8 = class _BaseECIESUint8 { // Cache for validated public keys to avoid repeated validation static validatedKeys = /* @__PURE__ */ new WeakMap(); /** * Normalizes a public key to uncompressed format. * * @param publicKey - Public key in any format. * @returns Uncompressed public key (65 bytes). * @throws {ECIESError} If key format is invalid. */ normalizePublicKey(publicKey) { if (_BaseECIESUint8.validatedKeys.get(publicKey)) { return publicKey; } if (publicKey.length === CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH) { if (publicKey[0] !== CURVE.PREFIX.UNCOMPRESSED) { throw new ECIESError( "Invalid uncompressed public key prefix", "INVALID_KEY" ); } if (!this.validatePublicKey(publicKey)) { throw new ECIESError("Invalid public key", "INVALID_KEY"); } _BaseECIESUint8.validatedKeys.set(publicKey, true); return publicKey; } if (publicKey.length === CURVE.COMPRESSED_PUBLIC_KEY_LENGTH) { if (publicKey[0] === CURVE.PREFIX.COMPRESSED_EVEN || publicKey[0] === CURVE.PREFIX.COMPRESSED_ODD) { const decompressed = this.decompressPublicKey(publicKey); if (!decompressed) { throw new ECIESError( "Failed to decompress public key", "INVALID_KEY" ); } _BaseECIESUint8.validatedKeys.set(decompressed, true); return decompressed; } throw new ECIESError( `Invalid compressed public key prefix: expected 0x02 or 0x03, got 0x${publicKey[0].toString(16).padStart(2, "0")}`, "INVALID_KEY" ); } throw new ECIESError( `Invalid public key length: ${publicKey.length}`, "INVALID_KEY" ); } /** * Encrypts data using ECIES. * * @param publicKey - The recipient's public key (compressed or uncompressed) * @param message - The data to encrypt * @returns Promise resolving to encrypted data structure */ async encrypt(publicKey, message) { let ephemeralPrivateKey; let sharedSecret; let kdf; let encryptionKey; let macKey; try { if (!(publicKey instanceof Uint8Array)) { throw new ECIESError("Public key must be a Uint8Array", "INVALID_KEY"); } if (!(message instanceof Uint8Array)) { throw new ECIESError( "Message must be a Uint8Array", "ENCRYPTION_FAILED" ); } if (publicKey.length === 0) { throw new ECIESError("Public key cannot be empty", "INVALID_KEY"); } const pubKey = this.normalizePublicKey(publicKey); do { ephemeralPrivateKey = this.generateRandomBytes( CURVE.PRIVATE_KEY_LENGTH ); } while (!this.verifyPrivateKey(ephemeralPrivateKey)); const ephemeralPublicKey = this.createPublicKey( ephemeralPrivateKey, false ); if (!ephemeralPublicKey) { throw new ECIESError( "Failed to generate ephemeral public key", "ENCRYPTION_FAILED" ); } sharedSecret = this.performECDH(pubKey, ephemeralPrivateKey); kdf = this.sha512(sharedSecret); encryptionKey = kdf.slice( KDF.ENCRYPTION_KEY_OFFSET, KDF.ENCRYPTION_KEY_OFFSET + KDF.ENCRYPTION_KEY_LENGTH ); macKey = kdf.slice( KDF.MAC_KEY_OFFSET, KDF.MAC_KEY_OFFSET + KDF.MAC_KEY_LENGTH ); const iv = this.generateRandomBytes(CIPHER.IV_LENGTH); const ciphertext = await this.aesEncrypt(encryptionKey, iv, message); const macData = (0, import_viem8.concat)([iv, ephemeralPublicKey, ciphertext]); const mac = this.hmacSha256(macKey, macData); return { iv, ephemPublicKey: ephemeralPublicKey, ciphertext, mac }; } catch (error) { if (error instanceof ECIESError) throw error; throw new ECIESError( `Encryption failed: ${error instanceof Error ? error.message : "Unknown error"}`, "ENCRYPTION_FAILED", error instanceof Error ? error : void 0 ); } finally { if (ephemeralPrivateKey) this.clearBuffer(ephemeralPrivateKey); if (sharedSecret) this.clearBuffer(sharedSecret); if (kdf) this.clearBuffer(kdf); if (encryptionKey) this.clearBuffer(encryptionKey); if (macKey) this.clearBuffer(macKey); } } /** * Decrypts ECIES encrypted data. * * @param privateKey - The recipient's private key (32 bytes) * @param encrypted - The encrypted data structure from encrypt() * @returns Promise resolving to the original plaintext */ async decrypt(privateKey, encrypted) { let sharedSecret; let kdf; let encryptionKey; let macKey; try { if (!(privateKey instanceof Uint8Array)) { throw new ECIESError("Private key must be a Uint8Array", "INVALID_KEY"); } if (!isECIESEncrypted(encrypted)) { throw new ECIESError( "Invalid encrypted data structure", "DECRYPTION_FAILED" ); } if (privateKey.length !== CURVE.PRIVATE_KEY_LENGTH) { throw new ECIESError( `Invalid private key length: ${privateKey.length}`, "INVALID_KEY" ); } if (!this.verifyPrivateKey(privateKey)) { throw new ECIESError("Invalid private key", "INVALID_KEY"); } if (encrypted.ephemPublicKey.length !== CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH) { throw new ECIESError( `Invalid ephemeral public key: expected ${CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH} bytes (uncompressed), got ${encrypted.ephemPublicKey.length} bytes`, "INVALID_KEY" ); } if (encrypted.ephemPublicKey[0] !== CURVE.PREFIX.UNCOMPRESSED) { throw new ECIESError( "Invalid ephemeral public key: must be uncompressed format with 0x04 prefix (eccrypto standard)", "INVALID_KEY" ); } if (!this.validatePublicKey(encrypted.ephemPublicKey)) { throw new ECIESError("Invalid ephemeral public key", "INVALID_KEY"); } const ephemeralPublicKey = encrypted.ephemPublicKey; sharedSecret = this.performECDH(ephemeralPublicKey, privateKey); kdf = this.sha512(sharedSecret); encryptionKey = kdf.slice( KDF.ENCRYPTION_KEY_OFFSET, KDF.ENCRYPTION_KEY_OFFSET + KDF.ENCRYPTION_KEY_LENGTH ); macKey = kdf.slice( KDF.MAC_KEY_OFFSET, KDF.MAC_KEY_OFFSET + KDF.MAC_KEY_LENGTH ); const macData = (0, import_viem8.concat)([ encrypted.iv, encrypted.ephemPublicKey, encrypted.ciphertext ]); const expectedMac = this.hmacSha256(macKey, macData); if (!constantTimeEqual(encrypted.mac, expectedMac)) { throw new ECIESError("MAC verification failed", "MAC_MISMATCH"); } const decrypted = await this.aesDecrypt( encryptionKey, encrypted.iv, encrypted.ciphertext ); return decrypted; } catch (error) { if (error instanceof ECIESError) throw error; throw new ECIESError( `Decryption failed: ${error instanceof Error ? error.message : "Unknown error"}`, "DECRYPTION_FAILED", error instanceof Error ? error : void 0 ); } finally { if (sharedSecret) this.clearBuffer(sharedSecret); if (kdf) this.clearBuffer(kdf); if (encryptionKey) this.clearBuffer(encryptionKey); if (macKey) this.clearBuffer(macKey); } } /** * Clears sensitive data from memory using multi-pass overwrite. * * @remarks * Uses multiple passes with different patterns to make it harder * for JIT compilers to optimize away the operation. While not * guaranteed in JavaScript, this is a best-effort approach to * clear sensitive data from memory. * * @param buffer - The buffer to clear */ clearBuffer(buffer) { if (buffer && buffer.length > 0) { buffer.fill(0); buffer.fill(255); buffer.fill(170); buffer.fill(0); for (let i = 0; i < buffer.length; i++) { buffer[i] = i & 255 ^ 90; } buffer.fill(0); } } }; } }); // src/crypto/ecies/browser.ts var secp256k12, import_viem10, import_hmac2, import_sha23, BrowserECIESUint8Provider; var init_browser = __esm({ "src/crypto/ecies/browser.ts"() { "use strict"; secp256k12 = __toESM(require("@noble/secp256k1"), 1); init_base(); import_viem10 = require("viem"); import_hmac2 = require("@noble/hashes/hmac"); import_sha23 = require("@noble/hashes/sha2"); BrowserECIESUint8Provider = class extends BaseECIESUint8 { generateRandomBytes(length) { const bytes = new Uint8Array(length); crypto.getRandomValues(bytes); return bytes; } verifyPrivateKey(privateKey) { try { return secp256k12.utils.isValidPrivateKey(privateKey); } catch { return false; } } createPublicKey(privateKey, compressed) { try { return secp256k12.getPublicKey(privateKey, compressed); } catch { return null; } } validatePublicKey(publicKey) { try { secp256k12.Point.fromHex(publicKey); return true; } catch { return false; } } decompressPublicKey(publicKey) { try { const point = secp256k12.Point.fromHex(publicKey); return point.toRawBytes(false); } catch { return null; } } performECDH(publicKey, privateKey) { try { const sharedPoint = secp256k12.getSharedSecret( privateKey, publicKey, true ); return sharedPoint.slice(1); } catch (error) { throw new Error( `ECDH failed: ${error instanceof Error ? error.message : "Unknown error"}` ); } } sha512(data) { return (0, import_sha23.sha512)(data); } hmacSha256(key, data) { return (0, import_hmac2.hmac)(import_sha23.sha256, key, data); } async aesEncrypt(key, iv, plaintext) { const cryptoKey = await crypto.subtle.importKey( "raw", key, { name: "AES-CBC" }, false, ["encrypt"] ); const encrypted = await crypto.subtle.encrypt( { name: "AES-CBC", iv }, cryptoKey, plaintext ); return new Uint8Array(encrypted); } async aesDecrypt(key, iv, ciphertext) { const cryptoKey = await crypto.subtle.importKey( "raw", key, { name: "AES-CBC" }, false, ["decrypt"] ); const decrypted = await crypto.subtle.decrypt( { name: "AES-CBC", iv }, cryptoKey, ciphertext ); return new Uint8Array(decrypted); } /** * Normalizes a public key to uncompressed format (65 bytes with 0x04 prefix). * Handles compressed (33 bytes) and uncompressed (65 bytes) formats only. * * @remarks * Strict policy: Does not accept 64-byte raw coordinates to avoid masking * malformed data. Callers must provide properly formatted keys. * * @param publicKey - The public key to normalize (33 or 65 bytes) * @returns The normalized uncompressed public key (65 bytes) * @throws {Error} When public key format is invalid or decompression fails */ normalizeToUncompressed(publicKey) { const len = publicKey.length; if (len === 65 && publicKey[0] === 4) { return publicKey; } if (len === 33 && (publicKey[0] === 2 || publicKey[0] === 3)) { const decompressed = this.decompressPublicKey(publicKey); if (!decompressed) { throw new Error( `Failed to decompress public key with prefix ${(0, import_viem10.toHex)(publicKey[0])}` ); } return decompressed; } if (len === 64) { throw new Error( "Raw public key coordinates (64 bytes) are not accepted. Please provide a properly formatted compressed (33 bytes) or uncompressed (65 bytes) public key." ); } throw new Error( `Invalid public key format: expected compressed (33 bytes) or uncompressed (65 bytes), got ${len} bytes` ); } }; } }); // src/platform/browser.ts var browser_exports = {}; __export(browser_exports, { BrowserPlatformAdapter: () => BrowserPlatformAdapter }); var import_viem11, secp256k13, getOpenPGP2, BrowserCryptoAdapter, BrowserPGPAdapter, BrowserHttpAdapter, BrowserCacheAdapter, BrowserPlatformAdapter; var init_browser2 = __esm({ "src/platform/browser.ts"() { "use strict"; init_pgp_utils(); init_error_utils(); init_lazy_import(); init_WalletKeyEncryptionService(); init_crypto_utils(); import_viem11 = require("viem"); secp256k13 = __toESM(require("@noble/secp256k1"), 1); init_browser(); getOpenPGP2 = lazyImport(() => import("openpgp")); BrowserCryptoAdapter = class { 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 = (0, import_viem11.fromHex)(prefixedHex, "bytes"); const encrypted = await this.eciesProvider.encrypt( publicKeyBytes, (0, import_viem11.stringToBytes)(data) ); const result = (0, import_viem11.concat)([ encrypted.iv, encrypted.ephemPublicKey, encrypted.ciphertext, encrypted.mac ]); return (0, import_viem11.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 = (0, import_viem11.fromHex)(encryptedHex, "bytes"); const privateKeyBytes = (0, import_viem11.fromHex)(privateHex, "bytes"); const encrypted = parseEncryptedDataBuffer(encryptedBytes); const decrypted = await this.eciesProvider.decrypt( privateKeyBytes, encrypted ); return (0, import_viem11.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 = secp256k13.utils.randomPrivateKey(); const publicKeyBytes = secp256k13.getPublicKey(privateKeyBytes, true); return { privateKey: (0, import_viem11.toHex)(privateKeyBytes).slice(2), publicKey: (0, import_viem11.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 openpgp2 = await getOpenPGP2(); const message = await openpgp2.createMessage({ binary: data }); const encrypted = await openpgp2.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 openpgp2 = await getOpenPGP2(); const message = await openpgp2.readMessage({ binaryMessage: encryptedData }); const { data } = await openpgp2.decrypt({ message, passwords: [password], format: "binary" }); return new Uint8Array(data); } catch (error) { throw wrapCryptoError("decryptWithPassword", error); } } }; BrowserPGPAdapter = class { /** * 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 openpgp2 = await getOpenPGP2(); const publicKey = await openpgp2.readKey({ armoredKey: publicKeyArmored }); const encrypted = await openpgp2.encrypt({ message: await openpgp2.createMessage({ text: data }), encryptionKeys: publicKey, config: { preferredCompressionAlgorithm: openpgp2.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 openpgp2 = await getOpenPGP2(); const privateKey = await openpgp2.readPrivateKey({ armoredKey: privateKeyArmored }); const message = await openpgp2.readMessage({ armoredMessage: encryptedData }); const { data: decrypted } = await openpgp2.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 openpgp2 = await getOpenPGP2(); const keyGenParams = getPGPKeyGenParams(options); const { privateKey, publicKey } = await openpgp2.generateKey(keyGenParams); return { publicKey, privateKey }; } catch (error) { throw wrapCryptoError("PGP key generation", error); } } }; BrowserHttpAdapter = class { /** * 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); } }; BrowserCacheAdapter = class { 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 { } } }; BrowserPlatformAdapter = class { crypto = new BrowserCryptoAdapter(); pgp = new BrowserPGPAdapter(); http = new BrowserHttpAdapter(); cache = new BrowserCacheAdapter(); platform = "browser"; }; } }); // src/index.node.ts var index_node_exports = {}; __export(index_node_exports, { ACCOUNT_PERSONAL_SERVER_REGISTRATION_INTENT: () => ACCOUNT_PERSONAL_SERVER_REGISTRATION_INTENT, AccountPersonalServerLiteOwnerBindingError: () => AccountPersonalServerLiteOwnerBindingError, AccountPersonalServerRegistrationError: () => AccountPersonalServerRegistrationError, BUILDER_REGISTRATION_TYPES: () => BUILDER_REGISTRATION_TYPES, BlockchainError: () => BlockchainError, BrowserPlatformAdapter: () => BrowserPlatformAdapter, CONTRACTS: () => CONTRACTS, CallbackStorage: () => CallbackStorage, ContractFactory: () => ContractFactory, ContractNotFoundError: () => ContractNotFoundError, DataFileEnvelopeSchema: () => DataFileEnvelopeSchema, DropboxStorage: () => DropboxStorage, ECIESError: () => ECIESError, ExpiredTokenError: () => ExpiredTokenError, FILE_REGISTRATION_TYPES: () => FILE_REGISTRATION_TYPES, GRANT_REGISTRATION_TYPES: () => GRANT_REGISTRATION_TYPES, GRANT_REVOCATION_TYPES: () => GRANT_REVOCATION_TYPES, GoogleDriveStorage: () => GoogleDriveStorage, InMemoryTokenStore: () => InMemoryTokenStore, IngestResponseSchema: () => IngestResponseSchema, InvalidConfigurationError: () => InvalidConfigurationError, InvalidSignatureError: () => InvalidSignatureError, IpfsStorage: () => IpfsStorage, MASTER_KEY_MESSAGE: () => MASTER_KEY_MESSAGE, MissingAuthError: () => MissingAuthError, NetworkError: () => NetworkError, NodeECIESProvider: () => NodeECIESUint8Provider, NodePlatformAdapter: () => NodePlatformAdapter, NonceError: () => NonceError, OAuthClient: () => OAuthClient, PERSONAL_SERVER_LITE_OWNER_BINDING_PREFIX: () => PERSONAL_SERVER_LITE_OWNER_BINDING_PREFIX, PERSONAL_SERVER_LITE_OWNER_BINDING_PURPOSE: () => PERSONAL_SERVER_LITE_OWNER_BINDING_PURPOSE, PERSONAL_SERVER_LITE_OWNER_BINDING_VERSION: () => PERSONAL_SERVER_LITE_OWNER_BINDING_VERSION, PERSONAL_SERVER_REGISTRATION_DEFAULT_CHAIN_ID: () => PERSONAL_SERVER_REGISTRATION_DEFAULT_CHAIN_ID, PERSONAL_SERVER_REGISTRATION_DEFAULT_VERIFYING_CONTRACT: () => PERSONAL_SERVER_REGISTRATION_DEFAULT_VERIFYING_CONTRACT, PKCE_CHALLENGE_PATTERN: () => PKCE_CHALLENGE_PATTERN, PKCE_VERIFIER_PATTERN: () => PKCE_VERIFIER_PATTERN, PSError: () => PSError, PermissionError: () => PermissionError, PersonalServerError: () => PersonalServerError, PinataStorage: () => PinataStorage, R2Storage: () => R2Storage, ReadOnlyError: () => ReadOnlyError, RelayerError: () => RelayerError, SERVER_REGISTRATION_TYPES: () => SERVER_REGISTRATION_TYPES, ScopeSchema: () => ScopeSchema, SerializationError: () => SerializationError, ServerUrlMismatchError: () => ServerUrlMismatchError, SignatureError: () => SignatureError, StorageError: () => StorageError, StorageManager: () => StorageManager, TransactionPendingError: () => TransactionPendingError, UserRejectedRequestError: () => UserRejectedRequestError, VanaError: () => VanaError, VanaStorage: () => VanaStorage, assertValidPkceVerifier: () => assertValidPkceVerifier, buildPersonalServerLiteOwnerBindingMessage: () => buildPersonalServerLiteOwnerBindingMessage, buildPersonalServerLiteOwnerBindingSignature: () => buildPersonalServerLiteOwnerBindingSignature, buildPersonalServerRegistrationSignature: () => buildPersonalServerRegistrationSignature, buildPersonalServerRegistrationTypedData: () => buildPersonalServerRegistrationTypedData, buildWeb3SignedHeader: () => buildWeb3SignedHeader, builderRegistrationDomain: () => builderRegistrationDomain, chains: () => chains, clearContractCache: () => clearContractCache, computeBodyHash: () => computeBodyHash, computePkceChallenge: () => computePkceChallenge, contractCacheForTesting: () => contractCacheForTesting, createBrowserPlatformAdapter: () => createBrowserPlatformAdapter, createDataFileEnvelope: () => createDataFileEnvelope, createGatewayClient: () => createGatewayClient, createNodePlatformAdapter: () => createNodePlatformAdapter, createPlatformAdapter: () => createPlatformAdapter, createPlatformAdapterFor: () => createPlatformAdapterFor, createPlatformAdapterSafe: () => createPlatformAdapterSafe, createVanaStorageProvider: () => createVanaStorageProvider, createViemPersonalServerLiteOwnerBindingSigner: () => createViemPersonalServerLiteOwnerBindingSigner, createViemPersonalServerRegistrationSigner: () => createViemPersonalServerRegistrationSigner, decryptWithPassword: () => decryptWithPassword, deriveMasterKey: () => deriveMasterKey, deriveScopeKey: () => deriveScopeKey, deserializeECIES: () => deserializeECIES, detectPlatform: () => detectPlatform, encryptWithPassword: () => encryptWithPassword, fileRegistrationDomain: () => fileRegistrationDomain, generatePkceVerifier: () => generatePkceVerifier, getAbi: () => getAbi, getAllChains: () => getAllChains, getChainConfig: () => getChainConfig, getContractAddress: () => getContractAddress, getContractController: () => getContractController, getContractInfo: () => getContractInfo, getPlatformCapabilities: () => getPlatformCapabilities, getServiceEndpoints: () => getServiceEndpoints, grantRegistrationDomain: () => grantRegistrationDomain, grantRevocationDomain: () => grantRevocationDomain, isDataPortabilityGatewayConfig: () => isDataPortabilityGatewayConfig, isECIESEncrypted: () => isECIESEncrypted, isPlatformSupported: () => isPlatformSupported, mainnetServices: () => mainnetServices, moksha: () => moksha, mokshaServices: () => mokshaServices, mokshaTestnet: () => mokshaTestnet2, parseGrantRegistrationPayload: () => parseGrantRegistrationPayload, parsePSError: () => parsePSError, parseScope: () => parseScope, parseWeb3SignedHeader: () => parseWeb3SignedHeader, personalServerRegistrationDomain: () => personalServerRegistrationDomain, recoverServerOwner: () => recoverServerOwner, registerPersonalServerSignature: () => registerPersonalServerSignature, scopeCoveredByGrant: () => scopeCoveredByGrant, scopeMatchesPattern: () => scopeMatchesPattern, scopeToPathSegments: () => scopeToPathSegments, serializeECIES: () => serializeECIES, serverRegistrationDomain: () => serverRegistrationDomain, signPersonalServerLiteOwnerBinding: () => signPersonalServerLiteOwnerBinding, signPersonalServerLiteOwnerBindingWithAccountClient: () => signPersonalServerLiteOwnerBindingWithAccountClient, signPersonalServerRegistrationWithAccount: () => signPersonalServerRegistrationWithAccount, vanaMainnet: () => vanaMainnet2, verifyGrantRegistration: () => verifyGrantRegistration, verifyPkceChallenge: () => verifyPkceChallenge, verifyWeb3Signed: () => verifyWeb3Signed }); module.exports = __toCommonJS(index_node_exports); // src/errors.ts var VanaError = class extends Error { constructor(message, code) { super(message); this.code = code; this.name = this.constructor.name; if (Error.captureStackTrace) { Error.captureStackTrace(this, this.constructor); } } code; }; var RelayerError = class extends VanaError { constructor(message, statusCode, response) { super(message, "RELAYER_ERROR"); this.statusCode = statusCode; this.response = response; } statusCode; response; }; var UserRejectedRequestError = class extends VanaError { constructor(message = "User rejected the signature request") { super(message, "USER_REJECTED_REQUEST"); } }; var InvalidConfigurationError = class extends VanaError { constructor(message) { super(message, "INVALID_CONFIGURATION"); } }; var ContractNotFoundError = class extends VanaError { constructor(contractName, chainId) { super( `Contract ${contractName} not found on chain ${chainId}`, "CONTRACT_NOT_FOUND" ); } }; var BlockchainError = class extends VanaError { constructor(message, originalError) { super(message, "BLOCKCHAIN_ERROR"); this.originalError = originalError; } originalError; }; var SerializationError = class extends VanaError { constructor(message) { super(message, "SERIALIZATION_ERROR"); } }; var SignatureError = clas